Skip to main content

Terminal adapter for apcore — execute AI-Perceivable modules from the command line

Project description

apcore-cli logo

apcore-cli

Terminal adapter for apcore. Execute AI-Perceivable modules from the command line.

License Python Tests

Python SDK github.com/aiperceivable/apcore-cli-python
Spec repo github.com/aiperceivable/apcore-cli
apcore core github.com/aiperceivable/apcore

apcore-cli turns any apcore-based project into a fully featured CLI tool — with zero code changes to your existing modules.

┌──────────────────┐
│  django-apcore   │  <- your existing apcore project (unchanged)
│  flask-apcore    │
│  ...             │
└────────┬─────────┘
         │  extensions directory
         v
┌──────────────────┐
│   apcore-cli     │  <- just install & point to extensions dir
└───┬──────────┬───┘
    │          │
    v          v
 Terminal    Unix
 Commands    Pipes

Design Philosophy

  • Zero intrusion -- your apcore project needs no code changes, no imports, no dependencies on apcore-cli
  • Zero configuration -- point to an extensions directory, everything is auto-discovered
  • Pure adapter -- apcore-cli reads from the apcore Registry; it never modifies your modules
  • Unix-native -- JSON output for pipes, rich tables for terminals, STDIN input, shell completions

Installation

pip install apcore-cli

Requires Python 3.11+ and apcore >= 0.17.1.

Quick Start

Try it now

The repo includes 8 example modules you can run immediately:

git clone https://github.com/aiperceivable/apcore-cli-python.git
cd apcore-cli-python
pip install -e ".[dev]"

# Run a module
apcore-cli --extensions-dir examples/extensions math add --a 5 --b 10
# {"sum": 15}

# List all modules
apcore-cli --extensions-dir examples/extensions apcli list --format json

# Run all examples
bash examples/run_examples.sh

See Examples for the full list of example modules and usage patterns.

Zero-code approach

If you already have an apcore-based project with an extensions directory:

# Execute a module
apcore-cli --extensions-dir ./extensions math add --a 42 --b 58

# Or set the env var once
export APCORE_EXTENSIONS_ROOT=./extensions
apcore-cli math add --a 42 --b 58

All modules are auto-discovered. CLI flags are auto-generated from each module's JSON Schema.

Programmatic approach (Python API)

from apcore_cli import create_cli

# Build the CLI from an extensions directory (auto-discovers modules)
cli = create_cli(extensions_dir="./extensions")
cli(standalone_mode=True)

Pre-populated registry

Frameworks that register modules at runtime (e.g. apflow's bridge) can pass a pre-populated Registry directly, skipping filesystem discovery entirely:

from apcore_cli import create_cli

# registry is already populated by your framework
cli = create_cli(registry=registry, prog_name="myapp")
cli(standalone_mode=True)

# Executor is auto-built from the registry if omitted.
# You can also provide your own:
cli = create_cli(registry=registry, executor=executor, prog_name="myapp")

Python API

The apcore_cli package exposes three public symbols:

Export Description
__version__ Package version string
create_cli(...) Factory that builds a ready-to-invoke click.Group. See the create_cli reference in the docs site for the full 8-parameter signature (extensions_dir, prog_name, commands_dir, binding_path, registry, executor, extra_commands, expose).
ExposureFilter Declarative filter controlling which modules are exposed by the CLI (FE-12).

Exposure filtering — restrict which modules are visible at the CLI layer without touching the underlying registry:

from apcore_cli import create_cli, ExposureFilter

# Only expose admin.* modules
cli = create_cli(
    registry=registry,
    executor=executor,
    expose=ExposureFilter(mode="include", include=["admin.*"]),
)

# Or pass a config dict (equivalent)
cli = create_cli(
    registry=registry,
    executor=executor,
    expose={"mode": "exclude", "exclude": ["debug.*", "test.*"]},
)

ExposureFilter supports mode="all" (default), "include", and "exclude", plus ExposureFilter.from_config(config) for dict-based construction, .is_exposed(module_id), and .filter_modules(module_ids).

Injecting custom commands — pass extra_commands=[...] to attach arbitrary Click commands alongside the auto-generated module commands (FE-11 extension point):

cli = create_cli(registry=registry, executor=executor, extra_commands=[my_custom_click_cmd])

Or use the LazyModuleGroup directly with Click:

import click
from apcore import Registry, Executor
from apcore_cli.cli import LazyModuleGroup

registry = Registry(extensions_dir="./extensions")
registry.discover()
executor = Executor(registry)

@click.group(cls=LazyModuleGroup, registry=registry, executor=executor)
def cli():
    pass

cli()

Adding Custom Commands

Fastest way (30 seconds)

apcore-cli apcli init module ops.deploy -d "Deploy to environment"
# Edit the generated file, add your logic

Zero-import way (convention discovery)

Drop a plain Python function into commands/:

# commands/deploy.py
def deploy(env: str, tag: str = "latest") -> dict:
    """Deploy the app to the given environment."""
    return {"status": "deployed", "env": env}

Then run with --commands-dir commands/:

apcore-cli --commands-dir commands/ deploy deploy --env prod

The init module command supports three styles via --style:

  • convention (default) — generates a plain Python function in the commands directory
  • decorator — generates a @module-decorated function in the extensions directory
  • binding — generates a .binding.yaml file

Integration with Existing Projects

Typical apcore project structure

your-project/
├── extensions/          <- modules live here
│   ├── math/
│   │   └── add.py
│   ├── text/
│   │   └── upper.py
│   └── ...
├── your_app.py          <- your existing code (untouched)
└── ...

Adding CLI support

No changes to your project. Just install and run:

pip install apcore-cli
apcore-cli --extensions-dir ./extensions apcli list
apcore-cli --extensions-dir ./extensions math add --a 5 --b 10

STDIN piping (Unix pipes)

# Pipe JSON input
echo '{"a": 100, "b": 200}' | apcore-cli math add --input -
# {"sum": 300}

# CLI flags override STDIN values
echo '{"a": 1, "b": 2}' | apcore-cli math add --input - --a 999
# {"sum": 1001}

# Chain with other tools
apcore-cli sysutil info | jq '.os, .hostname'

CLI Reference

apcore-cli [OPTIONS] COMMAND [ARGS]

Global Options

Option Default Description
--extensions-dir ./extensions Path to apcore extensions directory
--log-level WARNING Logging: DEBUG, INFO, WARNING, ERROR
--version Show version and exit
--help Show help and exit
--verbose Show hidden built-in options in --help output
--man Print man page to stdout (use with --help)

Built-in Commands

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.

Module invocation

Command Description
apcli exec <module_id> Execute a module (delegates to Executor.call())
apcli list List registered modules (supports --tag/--search/--status/--annotation/--sort/--reverse/--deprecated/--deps filters)
apcli describe <module_id> Show full module metadata and schema

System management (FE-11, v0.6.0)

Command Description
apcli config get/set <key> Read or update runtime config values
apcli health [<module_id>] Show module health status
apcli usage [<module_id>] Show module usage statistics
apcli enable <module_id> Enable a disabled module
apcli disable <module_id> Disable a module at runtime
apcli reload <module_id> Hot-reload a module from disk

Workflow

Command Description
apcli validate <module_id> Preflight-check a module without executing it (--dry-run)
apcli describe-pipeline Show execution pipeline steps for a strategy (FE-11)
apcli init module <module_id> Scaffold a new module (--style decorator|convention|binding)

Shell integration

Command Description
apcli completion <shell> Generate shell completion script (bash/zsh/fish)
apcli man <command> Generate roff man page for a command

Module Execution Options

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):

Option Description
--input - Read JSON input from STDIN
--yes / -y Bypass approval prompts
--large-input Allow STDIN input larger than 10MB
--format Output format: {json, table, csv, yaml, jsonl}
--sandbox Run module in subprocess sandbox (not yet implemented)
--dry-run Run preflight checks without executing (FE-11, v0.6.0)
--trace Emit execution pipeline trace (v0.6.0)
--stream Stream results line-by-line for stream-capable modules (v0.6.0)
--strategy <name> Override execution strategy: standard/internal/testing/performance/minimal (v0.6.0)
--fields <csv> Select specific output fields using dot-path notation (v0.6.0)
--approval-timeout <seconds> Override approval timeout (default 60) (v0.6.0)
--approval-token <token> Provide a pre-obtained approval token (v0.6.0)

Schema-generated flags (e.g. --a, --b) are added automatically from the module's input_schema.

The list command gained several discovery filters in v0.6.0: --search, --status, --annotation, --sort, --reverse, --deprecated, and --deps.

Exit Codes

Code Meaning
0 Success
1 Module execution error
2 Invalid CLI input
44 Module not found / disabled / load error
45 Schema validation error
46 Approval denied or timed out
47 Configuration error
48 Schema circular reference
77 ACL denied
130 Execution cancelled (Ctrl+C)

Configuration

apcore-cli uses a 4-tier configuration precedence:

  1. CLI flag (highest): --extensions-dir ./custom
  2. Environment variable: APCORE_EXTENSIONS_ROOT=./custom
  3. Config file: apcore.yaml
  4. Default (lowest): ./extensions

Environment Variables

Variable Description Default
APCORE_EXTENSIONS_ROOT Path to extensions directory ./extensions
APCORE_CLI_AUTO_APPROVE Set to 1 to bypass all approval prompts (unset)
APCORE_CLI_LOGGING_LEVEL CLI-specific log level (takes priority over APCORE_LOGGING_LEVEL) WARNING
APCORE_LOGGING_LEVEL Global apcore log level (fallback when APCORE_CLI_LOGGING_LEVEL is unset) WARNING
APCORE_AUTH_API_KEY API key for remote registry authentication (unset)
APCORE_CLI_SANDBOX Set to 1 to enable subprocess sandboxing (unset)
APCORE_CLI_HELP_TEXT_MAX_LENGTH Maximum characters for CLI option help text before truncation 1000
APCORE_CLI_APPROVAL_TIMEOUT Approval-gate timeout in seconds (v0.6.0) 60
APCORE_CLI_STRATEGY Default execution strategy (v0.6.0) standard
APCORE_CLI_GROUP_DEPTH Depth of automatic command grouping by . segments (v0.6.0) 1

Config File (apcore.yaml)

extensions:
  root: ./extensions
logging:
  level: DEBUG
sandbox:
  enabled: false
cli:
  help_text_max_length: 1000
  approval_timeout: 60     # v0.6.0
  strategy: standard       # v0.6.0
  group_depth: 1           # v0.6.0

Features

  • Auto-discovery -- all modules in the extensions directory are found and exposed as CLI commands
  • Display overlay -- metadata["display"]["cli"] controls CLI command names, descriptions, and guidance per module (§5.13); set via binding_path in create_cli() / fastapi-apcore
  • Grouped commands -- modules with dots in their names are auto-grouped into nested subcommands (apcore-cli product list instead of apcore-cli product.list); display.cli.group in binding.yaml overrides the auto-detected group
  • Auto-generated flags -- JSON Schema input_schema is converted to --flag value CLI options with type validation
  • Boolean flag pairs -- --verbose / --no-verbose from "type": "boolean" schema properties
  • Enum choices -- "enum": ["json", "csv"] becomes --format json with Click validation
  • STDIN piping -- --input - reads JSON from STDIN, CLI flags override for duplicate keys
  • TTY-adaptive output -- rich tables for terminals, JSON for pipes (configurable via --format)
  • Approval gate -- TTY-aware HITL prompts for modules with requires_approval: true, with --yes bypass and 60s timeout
  • Schema validation -- inputs validated against JSON Schema before execution, with $ref/allOf/anyOf/oneOf resolution
  • Security -- API key auth (keyring + AES-256-GCM), append-only audit logging, subprocess sandboxing
  • Shell completions -- apcore-cli apcli completion bash|zsh|fish generates completion scripts with dynamic module ID completion
  • Man pages -- apcore-cli apcli man <command> generates per-command man pages; --help --man prints a full-program man page via configure_man_help()
  • 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
  • Audit logging -- all executions logged to ~/.apcore-cli/audit.jsonl with SHA-256 input hashing

How It Works

Mapping: apcore to CLI

apcore CLI
metadata["display"]["cli"]["alias"] or module_id Command name — auto-grouped by first . segment (apcore-cli product get)
metadata["display"]["cli"]["description"] or description --help text
input_schema.properties CLI flags (--a, --b)
input_schema.required Validated post-collection via jsonschema.validate() (required fields shown as [required] in --help)
annotations.requires_approval HITL approval prompt

Architecture

User / AI Agent (terminal)
    |
    v
apcore-cli (the adapter)
    |
    +-- ConfigResolver            4-tier config precedence
    +-- GroupedModuleGroup (default)  Multi-level command grouping (wraps LazyModuleGroup)
    +-- LazyModuleGroup           Lazy per-module Click command construction (base class)
    +-- ExposureFilter            Declarative module exposure filtering (FE-12)
    +-- CliApprovalHandler        Async approval handler protocol implementation (FE-11)
    +-- system_cmd                Runtime system-management (health/usage/enable/disable/reload/config)
    +-- strategy                  Execution strategy dispatch (--strategy flag and describe-pipeline)
    +-- init_cmd                  Module scaffolding (init subcommand)
    +-- set_verbose_help          Toggle built-in option visibility
    +-- set_docs_url              Set base URL for online docs
    +-- build_program_man_page    Full-program roff man page
    +-- configure_man_help        Add --help --man support to any CLI
    +-- schema_parser             JSON Schema -> Click options
    +-- ref_resolver              $ref / allOf / anyOf / oneOf
    +-- approval                  TTY-aware HITL approval
    +-- output                    TTY-adaptive JSON/table output
    +-- AuditLogger               JSON Lines execution logging
    +-- Sandbox                   Subprocess isolation
    |
    v
apcore Registry + Executor (your modules, unchanged)

Examples

The examples/extensions/ directory contains 8 runnable modules:

Module Description Usage
math.add Add two integers apcore-cli math add --a 5 --b 10
math.multiply Multiply two integers apcore-cli math multiply --a 6 --b 7
text.upper Uppercase a string apcore-cli text upper --text hello
text.reverse Reverse a string apcore-cli text reverse --text abcdef
text.wordcount Count words/chars/lines apcore-cli text wordcount --text "hello world"
sysutil.info OS, hostname, Python version apcore-cli sysutil info
sysutil.env Read environment variables apcore-cli sysutil env --name HOME
sysutil.disk Disk usage statistics apcore-cli sysutil disk --path /

Running examples

# Set extensions path (one time)
export APCORE_EXTENSIONS_ROOT=examples/extensions

# Execute modules
apcore-cli math add --a 42 --b 58
apcore-cli text upper --text "hello apcore"
apcore-cli sysutil info
apcore-cli sysutil disk --path /

# Discovery
apcore-cli apcli list --format json
apcore-cli apcli list --tag math --format json
apcore-cli apcli describe math.add --format json

# STDIN piping
echo '{"a": 100, "b": 200}' | apcore-cli math add --input -

# Shell completion
apcore-cli apcli completion bash >> ~/.bashrc
apcore-cli apcli completion zsh >> ~/.zshrc
apcore-cli apcli completion fish > ~/.config/fish/completions/apcore-cli.fish

# Man pages
apcore-cli apcli man list | man -l -

# Run all examples at once
bash examples/run_examples.sh

Writing your own module

Create a Python file in your extensions directory:

# extensions/greet/hello.py
from pydantic import BaseModel

class Input(BaseModel):
    name: str
    greeting: str = "Hello"

class Output(BaseModel):
    message: str

class GreetHello:
    input_schema = Input
    output_schema = Output
    description = "Greet someone by name"

    def execute(self, inputs, context=None):
        return {"message": f"{inputs['greeting']}, {inputs['name']}!"}

Then run it:

apcore-cli --extensions-dir ./extensions greet hello --name World
# {"message": "Hello, World!"}

apcore-cli --extensions-dir ./extensions greet hello --name Alice --greeting Hi
# {"message": "Hi, Alice!"}

Development

The conformance suite under tests/conformance/ reads shared fixtures from the spec repo (aiperceivable/apcore-cli). Clone it as a sibling of this repo, or point APCORE_CLI_SPEC_REPO at an existing checkout:

# One-time: clone both repos side by side
git clone https://github.com/aiperceivable/apcore-cli.git
git clone https://github.com/aiperceivable/apcore-cli-python.git

cd apcore-cli-python
pip install -e ".[dev]"
pytest                           # reads fixtures from ../apcore-cli/conformance/
pytest --cov                     # with coverage report
bash examples/run_examples.sh    # run all examples

Alternative layout (spec repo checked out elsewhere):

export APCORE_CLI_SPEC_REPO=/path/to/apcore-cli
pytest

Without the spec repo the conformance tests are skipped (pytest reports them as skipped, not failed). CI clones the spec repo automatically — see .github/workflows/ci.yml.

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apcore_cli-0.7.0.tar.gz (359.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apcore_cli-0.7.0-py3-none-any.whl (83.7 kB view details)

Uploaded Python 3

File details

Details for the file apcore_cli-0.7.0.tar.gz.

File metadata

  • Download URL: apcore_cli-0.7.0.tar.gz
  • Upload date:
  • Size: 359.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for apcore_cli-0.7.0.tar.gz
Algorithm Hash digest
SHA256 bda1f230f0f7ff9f4e258c04e8fd7c9b78c04204e4d104ae404fc3a9e75358e6
MD5 883f1c52c8d3dda419f5064b94e510c6
BLAKE2b-256 76f9f7f00a650b9c339e58c64511b510a60e88feefa816adb50cefe8f73f9020

See more details on using hashes here.

File details

Details for the file apcore_cli-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: apcore_cli-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 83.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for apcore_cli-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 96a5ba0c9fc5be122bc5f5cd70f334b1f5252131352053702442ff5f71f7b925
MD5 a559af95d7b56d638474d8c7ffdd1fb7
BLAKE2b-256 d3cc40bcfc994288a40cb36ad8881fc76101ac955cd23299fc3fe4315cfd5020

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page