This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the Cortex CLI (cortexapps-cli), a command-line interface for interacting with the Cortex API (https://cortex.io). The CLI is built with Python 3.11+ using the Typer framework and provides commands for managing catalog entities, scorecards, teams, workflows, integrations, and other Cortex resources.
# Install dependencies
poetry install
# Set required environment variables for testing
export CORTEX_API_KEY=<your-api-key>
export CORTEX_BASE_URL=https://api.getcortexapp.com # optional, defaults to this
export CORTEX_API_KEY_VIEWER=<viewer-api-key> # for viewer permission testsThe project uses just for task automation:
# Run all available recipes
just
# Run all tests (requires prior test-import)
just test-all
# Run import test (prerequisite for other tests - loads test data)
just test-import
# Run a single test file
just test tests/test_catalog.py# Run the CLI locally
poetry run cortex <command>
# Examples:
poetry run cortex catalog list
poetry run cortex -t my-tenant catalog listNot currently configured in this project.
- Main entry point:
cortexapps_cli/cli.py- Defines the main Typer app and global options - Command structure: Each major resource has its own command module in
cortexapps_cli/commands/ - Subcommands: Some commands have nested subcommands in subdirectories (e.g.,
backup_commands/,integrations_commands/,packages_commands/,scorecards_commands/)
All commands inherit global options defined in cli.py:global_callback():
-k, --api-key: API key (orCORTEX_API_KEYenv var)-u, --url: Base URL (orCORTEX_BASE_URLenv var)-c, --config: Config file path (defaults to~/.cortex/config)-t, --tenant: Tenant alias (defaults to "default")-l, --log-level: Logging level (defaults to INFO)
The CLI supports two authentication methods:
- Config file (
~/.cortex/config): INI-style file with sections per tenant - Environment variables:
CORTEX_API_KEYandCORTEX_BASE_URL
Environment variables take precedence over config file values.
CortexClient(cortexapps_cli/cortex_client.py): Core HTTP client that handles all API requests- Provides
get(),post(),put(),patch(),delete()methods fetch(): Auto-paginated fetch for list endpointsfetch_or_get(): Conditionally fetches all pages or single page based on parameters- Error handling with formatted error output using Rich
- Provides
Each command module follows a similar pattern:
- Creates a Typer app instance
- Defines command-specific option classes (following
CommandOptionspattern incommand_options.py) - Implements command functions decorated with
@app.command() - Commands receive
ctx: typer.Contextto access the sharedCortexClientviactx.obj["client"] - Uses utility functions from
utils.pyfor output formatting
-
ListCommandOptions(command_options.py): Standard options for list commands--table,--csv: Output format options--columns, -C: Select specific columns for table/csv output--filter, -F: Filter rows using JSONPath and regex--sort, -S: Sort rows by JSONPath fields--page, -p,--page-size, -z: Pagination controls
-
CommandOptions: Base options (e.g.,--printfor internal testing)
- Default: JSON output via Rich's
print_json() - Table/CSV output: Configurable via
--tableor--csvflags with column selection - Output utilities in
utils.py:print_output(): JSON outputprint_output_with_context(): Formatted output with table/csv supportguess_data_key(): Infers the data key in paginated responses
- Test framework: pytest with pytest-cov for coverage
- Test utilities:
tests/helpers/utils.pyprovides acli()helper that wraps the Typer CLI runner - Test data setup: Tests depend on
test_import.pyrunning first to load test entities - Tests use the real Cortex API (not mocked) and require valid
CORTEX_API_KEY - Parallel execution: Tests run with
pytest-xdist(-n auto) for speed - Serial marker: Use
@pytest.mark.serialfor tests that must run sequentially
Follow the conventions in STYLE.md:
- Flags over arguments: Use named flags for clarity and future compatibility
- Long and short versions: All flags should have both (e.g.,
--long-version, -l) - Consistent short flags: Reuse short flags across commands where possible
- Kebab-case: Multi-word flags use kebab-case (e.g.,
--api-key)
- list: Paginated list of resources (fetch all pages by default)
- get: Retrieve full details of a single object
- create: Create new object (fails if exists, unless
--replace-existingor--update-existing) - delete: Delete object (interactive prompt unless
--force) - update: Modify existing object (accepts full or partial definitions)
- archive/unarchive: Archive operations
- add/remove: Add/remove items from list attributes
- set/unset: Set/unset single-value attributes
- open: Open resource in browser
Use the GitHub-recommended format: <issue-number>-<short-description>
- Example:
186-fix-urllib3-cvefor issue #186 - Use lowercase kebab-case for the description
- Keep the description concise (3-5 words)
Documentation-only changes (like updates to CLAUDE.md, README.md, STYLE.md) can be committed directly to main without going through the staging workflow.
- Create feature branch for changes
- Create PR to merge feature branch to
stagingfor testing - Create PR to merge
stagingtomainto trigger release:gh pr create --base main --head staging --title "Release X.Y.Z: Description"- Include the expected version number and brief description in title
- List all changes in the PR body
- Version bumping is automatic based on conventional commit prefixes in the commit history since the last tag:
feat:prefix → minor version bump (new features)fix:prefix → patch version bump (bug fixes)- If multiple types present, the highest wins (feat > fix)
- Default (no recognized prefix): patch bump
- Release publishes to:
- PyPI
- Docker Hub (
cortexapp/cli:VERSIONandcortexapp/cli:latest) - Homebrew tap (
cortexapps/homebrew-tap)
Before creating a staging-to-main release PR, Claude must:
-
Check the current version tag:
git fetch origin --tags git describe --tags --abbrev=0
-
Analyze commits since the last tag:
git log <last-tag>..origin/staging --oneline
-
Determine the version bump by examining commit prefixes:
- Any
feat:commit → minor bump (X.Y.0 → X.(Y+1).0) - Only
fix:commits → patch bump (X.Y.Z → X.Y.(Z+1))
- Any
-
Explain the version to the user before creating the PR:
"The next version will be X.Y.Z because the commits include:
feat: add --table option to newrelic list(triggers minor bump)fix: correct API endpoint for validateSince there's afeat:commit, this will be a minor version bump."
This ensures the user understands what version will be published and why.
The mislav/bump-homebrew-formula-action only updates the main package URL and SHA256. It cannot update the resource blocks for Python dependencies (this is a documented limitation of the action).
When updating Python dependency versions (e.g., urllib3, requests), the homebrew formula in cortexapps/homebrew-tap must be updated manually:
- Clone the
cortexapps/homebrew-taprepository - Update the resource blocks in
Formula/cortexapps-cli.rbwith new URLs and SHA256 hashes from PyPI - Alternatively, use
brew update-python-resources cortexapps-clilocally and copy the output
Important: The homebrew/cortexapps-cli.rb file in this repository should be kept in sync with the tap formula for reference. Update it when making dependency changes.
Commits use conventional commit prefixes which affect both versioning and changelog:
For version bumping (detected by github-tag-action):
feat:→ triggers minor version bumpfix:→ triggers patch version bump
For HISTORY.md changelog (detected by git-changelog):
add:→ appears under "Added"fix:→ appears under "Bug Fixes"change:→ appears under "Changed"remove:→ appears under "Removed"
Best practice: Use feat: for new features (will bump minor) and fix: for bug fixes (will bump patch). These prefixes satisfy both the version bumping and changelog generation.
The HISTORY.md file is auto-generated when staging is merged to main. This means:
mainalways has the latest HISTORY.mdstaginglags behind until the next release- Feature branches created from
mainhave the updated history
When merging feature branches to staging, conflicts in HISTORY.md are expected. Resolve by accepting the incoming version:
git checkout --theirs HISTORY.md
git add HISTORY.mdThe HISTORY.md file is auto-generated when staging is merged to main. This means:
mainalways has the latest HISTORY.mdstaginglags behind until the next release- Feature branches created from
mainhave the updated history
When merging feature branches to staging, conflicts in HISTORY.md are expected. Resolve by accepting the incoming version:
git checkout --theirs HISTORY.md
git add HISTORY.mdpublish.yml: Triggered on push tomain, handles versioning and multi-platform publishingtest-pr.yml: Runs tests on pull requests
The main branch has push restrictions enabled to control who can merge:
- Allowed users:
jeff-schnitter - Allowed apps:
github-actions(sopublish.ymlcan push HISTORY.md updates) - No PR requirement: Disabled so that doc-only commits and CI-generated HISTORY.md changes can be pushed directly
- Force pushes: Blocked
- Branch deletion: Blocked
External contributors can fork the repo and open PRs, but only allowed users can merge or push to main.
cli.py: Main CLI entry point and global callbackcortex_client.py: HTTP client for Cortex APIcommand_options.py: Reusable command option definitionsutils.py: Output formatting utilitiescommands/*.py: Individual command implementationspyproject.toml: Poetry configuration and dependenciesJustfile: Task automation recipesDEVELOPER.md: Developer-specific testing and workflow notesSTYLE.md: Command design guidelines