Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
b0b9cd1
Clean up logic to load repository config items
ogenstad Mar 13, 2026
1ccef9b
Add tests
ogenstad Mar 13, 2026
a4c5ecb
Merge pull request #871 from opsmill/stable
ogenstad Mar 17, 2026
6caa67b
Merge pull request #872 from opsmill/develop
ogenstad Mar 17, 2026
ac6ccdf
Merge pull request #873 from opsmill/stable
gmazoyer Mar 17, 2026
d566793
IFC-2303: update SDK protocols for CoreKeyValue schema (#867)
polmichel Mar 17, 2026
f7266eb
Remove `raise_for_error` from several methods (#874)
gmazoyer Mar 17, 2026
cce6014
Merge pull request #866 from opsmill/pog-repository-loading
ogenstad Mar 17, 2026
9871167
IFC-2156: Remove proposed change comments created_at and created_by a…
solababs Mar 18, 2026
19a079e
Merge pull request #876 from opsmill/stable
petercrocker Mar 19, 2026
665cd24
Fix typing warnings with ty
ogenstad Mar 20, 2026
a75a893
Merge pull request #878 from opsmill/pog-fix-typing-warnings
ogenstad Mar 20, 2026
dc6b9c4
Merge pull request #880 from opsmill/stable
ogenstad Mar 23, 2026
1a1d0dd
Add support for GraphQL fragment inlining
ogenstad Mar 16, 2026
b525fef
Merge pull request #877 from opsmill/pog-specs-infp-496
ogenstad Mar 23, 2026
ced2533
Add specs for artifact composition (#836)
ogenstad Mar 25, 2026
aecc708
Add plan to implement INFP-504 (#885)
gmazoyer Mar 25, 2026
95db686
Merge pull request #890 from opsmill/stable
ogenstad Mar 25, 2026
d9af0fe
IFC-2275 Add Jinja2 filters for artifact content composition (#889)
gmazoyer Mar 25, 2026
5f6c7e7
Merge pull request #883 from opsmill/develop
ogenstad Mar 25, 2026
9b22e04
Merge pull request #893 from opsmill/stable
ogenstad Mar 26, 2026
2bd77e4
Merge stable into develop
bilalabbad Mar 26, 2026
334eefb
Merge pull request #899 from opsmill/stable
gmazoyer Mar 26, 2026
8a71161
Merge pull request #898 from opsmill/develop
ogenstad Mar 27, 2026
85502b4
feat: add end-user `infrahub` CLI for CRUD operations and schema disc…
petercrocker Mar 28, 2026
8fba90d
fix: resolve CI failures in markdown lint, integration tests, and docs
petercrocker Mar 28, 2026
94e3780
test: increase unit test coverage for end-user CLI
petercrocker Mar 28, 2026
e71acb1
fix: resolve remaining CI failures
petercrocker Mar 28, 2026
52c2b05
fix: resolve CI failures in integration tests, markdown lint, and docs
petercrocker Mar 28, 2026
a941e57
refactor: move end-user CLI commands under infrahubctl
petercrocker Mar 28, 2026
b8d30b4
fix: resolve doc generation and integration test failures
petercrocker Mar 28, 2026
4f465f9
fix: resolve vale spelling errors in generated CLI docs
petercrocker Mar 28, 2026
0c2ecc5
feat: hide empty columns by default in table and CSV output
petercrocker Mar 28, 2026
36332e3
feat: improve empty results UX for infrahubctl get
petercrocker Mar 28, 2026
7b7fb9f
feat: resolve nodes by UUID, default filter, or HFID
petercrocker Mar 28, 2026
c9119ea
fix: make YAML output round-trippable with infrahubctl object load
petercrocker Mar 28, 2026
ee6b73f
fix: clean error output for missing --set/--file arguments
petercrocker Mar 28, 2026
233ac0b
feat: resolve relationship values by name in create/update commands
petercrocker Mar 28, 2026
aca8c89
fix: show name instead of None in create confirmation message
petercrocker Mar 28, 2026
9f2813e
feat: distinguish create vs upsert in confirmation message
petercrocker Mar 28, 2026
0c730be
feat: skip save and show no-op message when update values unchanged
petercrocker Mar 28, 2026
db23a7d
docs: regenerate get command docs (--all-columns flag added)
petercrocker Mar 28, 2026
4633713
docs: add usage examples to all command help text
petercrocker Mar 28, 2026
0a3a408
fix: add 'yaml' to vale spelling exceptions
petercrocker Mar 28, 2026
4caff14
docs: clarify exit code 80 applies only to list mode, not detail lookups
petercrocker Mar 28, 2026
1b4b2bd
docs: document HFID support in identifier argument help text
petercrocker Mar 28, 2026
ff5c177
fix: compare relationship IDs not display strings for change detection
petercrocker Mar 28, 2026
419b13b
fix: warn that kind/identifier are ignored in update --file mode
petercrocker Mar 28, 2026
f1b73c0
fix: narrow exception handling in resolve_relationship_values
petercrocker Mar 28, 2026
93634c6
test: remove redundant @pytest.mark.anyio from resolve_node tests
petercrocker Mar 28, 2026
fa3688f
docs: fix contract to show exit code 80 for empty list results
petercrocker Mar 28, 2026
52f77ce
docs: update spec to reference infrahubctl instead of infrahub command
petercrocker Mar 28, 2026
432ba94
docs: update research.md to reflect infrahubctl integration decision
petercrocker Mar 28, 2026
2a8fb43
fix: preserve leading zeros in --set value coercion
petercrocker Mar 28, 2026
03714b7
test: add relationship no-op test for update command
petercrocker Mar 28, 2026
bc4bfb6
fix: ruff formatting for update.py console.print line
petercrocker Mar 28, 2026
52252ae
Merge pull request #902 from opsmill/stable
petercrocker Mar 29, 2026
86b5bab
Merge pull request #903 from opsmill/develop
ogenstad Mar 30, 2026
3e93fe2
Add file_object_content_by_id and file_object_content_by_hfid filters…
gmazoyer Mar 30, 2026
abe1d0d
Set `*_content` filter context to `WORKER` (#907)
gmazoyer Mar 30, 2026
dc28bad
Clean up of logic within Node
ogenstad Mar 31, 2026
4704159
Merge pull request #910 from opsmill/pog-node-cleanup
ogenstad Apr 1, 2026
28af520
Reduce cyclomatic complexity in generate_query_data_node
ogenstad Apr 1, 2026
fff91da
Merge pull request #912 from opsmill/pog-node-generate_query_data_nod…
ogenstad Apr 1, 2026
35cf000
Use RelationshipCardinality enum in favor of a string
ogenstad Apr 1, 2026
fc0bb09
Merge pull request #913 from opsmill/pog-use-relationship-cardinality…
ogenstad Apr 1, 2026
6614764
feat: IHS-190 (part of IFC-2162) Add namespace restriction parameter …
polmichel Apr 1, 2026
1d36d8d
rework
Apr 1, 2026
3c3f628
linter
Apr 1, 2026
8b314ea
sanitize
Apr 2, 2026
b74c893
coderabbit
Apr 2, 2026
9af2752
cubic
Apr 2, 2026
56e5a0c
filter by NodeSchemaAPI type before accessing .kind in schema list
petercrocker Apr 3, 2026
5554787
Merge pull request #900 from opsmill/001-end-user-cli
petercrocker Apr 3, 2026
2c82fa9
move specs/ to dev/specs/ and replace with symlink
petercrocker Apr 3, 2026
55788b2
remove specs symlink, update all references to dev/specs
petercrocker Apr 3, 2026
a9a6893
Merge pull request #915 from opsmill/fix/move-specs-to-dev-symlink
petercrocker Apr 3, 2026
aa01aa6
Merge pull request #914 from opsmill/develop
ajtmccarty Apr 3, 2026
2bfc100
Merge pull request #916 from opsmill/stable
ajtmccarty Apr 3, 2026
cee0e7f
Merge pull request #917 from opsmill/develop
ajtmccarty Apr 3, 2026
2258971
Merge pull request #919 from opsmill/stable
petercrocker Apr 6, 2026
ec74c54
Merge pull request #922 from opsmill/stable
ajtmccarty Apr 6, 2026
2e87b74
Merge pull request #920 from opsmill/develop
ogenstad Apr 7, 2026
3693289
Code cleanup
ogenstad Apr 8, 2026
5896afd
Merge pull request #924 from opsmill/pog-crud-cleanup
ogenstad Apr 8, 2026
76b584c
Merge pull request #925 from opsmill/stable
ogenstad Apr 8, 2026
95ceea8
Merge pull request #929 from opsmill/stable
ogenstad Apr 8, 2026
405b656
Merge pull request #926 from opsmill/develop
ogenstad Apr 8, 2026
78c6800
Fix authentication validation to allow password auth to override env …
Copilot Nov 20, 2025
ddb1b2c
Merge pull request #928 from opsmill/pog-sdk-auth-settings
ogenstad Apr 9, 2026
6d443d3
Move CRUD commands under object subgroup (#931)
gmazoyer Apr 9, 2026
91d7c77
Merge pull request #933 from opsmill/stable
ogenstad Apr 9, 2026
729e8ef
Move object commands into own package (#935)
gmazoyer Apr 10, 2026
0629c5a
Merge pull request #934 from opsmill/develop
ogenstad Apr 13, 2026
f9249a7
feat(infrahubctl): add telemetry commands
fatih-acar Apr 10, 2026
0233b90
Let the server validate in object CRUD commands (#942)
gmazoyer Apr 14, 2026
e7c4ee5
Merge pull request #947 from opsmill/develop
ajtmccarty Apr 14, 2026
44a7ccc
Make builtin Jinja2 filters allowed on workers (#943)
gmazoyer Apr 14, 2026
3305f85
Raise a new file not found error when loading queries
ogenstad Apr 15, 2026
5523e94
Merge pull request #950 from opsmill/pog-filenotfound-on-load-query
ogenstad Apr 17, 2026
a203aac
Add description field support to InfrahubPythonTransformConfig (#887)
minitriga Apr 20, 2026
e317d93
Fix file_object_content filters 404ing due to wrong URL prefix (#956)
PhillSimonds Apr 22, 2026
67a0061
Enforce filter execution-context gate in infrahubctl render (#957)
PhillSimonds Apr 22, 2026
2069066
Fix ObjectStore.get() and upload() swallowing HTTP errors other than …
PhillSimonds Apr 22, 2026
0d648c6
Merge pull request #936 from opsmill/stable
ogenstad Apr 22, 2026
e78b9da
Merge pull request #964 from opsmill/stable
ogenstad Apr 23, 2026
c065b3f
IHS-219: Add query_name parameter to all(), filters(), and get() for …
solababs Apr 23, 2026
096feba
Merge pull request #961 from opsmill/develop
ogenstad Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .specify/scripts/bash/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ get_current_branch() {

# For non-git repos, try to find the latest feature directory
local repo_root=$(get_repo_root)
local specs_dir="$repo_root/specs"
local specs_dir="$repo_root/dev/specs"

if [[ -d "$specs_dir" ]]; then
local latest_feature=""
Expand Down Expand Up @@ -81,14 +81,14 @@ check_feature_branch() {
return 0
}

get_feature_dir() { echo "$1/specs/$2"; }
get_feature_dir() { echo "$1/dev/specs/$2"; }

# Find feature directory by numeric prefix instead of exact branch match
# This allows multiple branches to work on the same spec (e.g., 004-fix-bug, 004-add-feature)
find_feature_dir_by_prefix() {
local repo_root="$1"
local branch_name="$2"
local specs_dir="$repo_root/specs"
local specs_dir="$repo_root/dev/specs"

# Extract numeric prefix from branch (e.g., "004" from "004-whatever")
if [[ ! "$branch_name" =~ ^([0-9]{3})- ]]; then
Expand All @@ -99,7 +99,7 @@ find_feature_dir_by_prefix() {

local prefix="${BASH_REMATCH[1]}"

# Search for directories in specs/ that start with this prefix
# Search for directories in dev/specs/ that start with this prefix
local matches=()
if [[ -d "$specs_dir" ]]; then
for dir in "$specs_dir"/"$prefix"-*; do
Expand Down
4 changes: 2 additions & 2 deletions .specify/templates/plan-template.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Implementation Plan: [FEATURE]

**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
**Input**: Feature specification from `/dev/specs/[###-feature-name]/spec.md`

**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.

Expand Down Expand Up @@ -38,7 +38,7 @@
### Documentation (this feature)

```text
specs/[###-feature]/
dev/specs/[###-feature]/
├── plan.md # This file (/speckit.plan command output)
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
Expand Down
2 changes: 1 addition & 1 deletion .specify/templates/tasks-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "Task list template for feature implementation"

# Tasks: [FEATURE NAME]

**Input**: Design documents from `/specs/[###-feature-name]/`
**Input**: Design documents from `/dev/specs/[###-feature-name]/`
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/

**Tests**: The examples below include test tasks. Tests are OPTIONAL - only include them if explicitly requested in the feature specification.
Expand Down
1 change: 1 addition & 0 deletions .vale/styles/spelling-exceptions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ validators
Version Control
Vitest
VLANs
yaml
Yaml
yamllint
YouTube
Expand Down
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ Key rules:
- Modify generated code (protocols.py)
- Bypass type checking without justification

## Knowledge base

Deep-dive docs on architecture and workflows live in `dev/knowledge/`. Read these before making changes to the areas they cover.

- [dev/knowledge/cli-architecture.md](dev/knowledge/cli-architecture.md) - CLI command hierarchy and design rules
- [dev/knowledge/cli-design-principles.md](dev/knowledge/cli-design-principles.md) - Principles for writing CLI commands (no pre-validation, phrasing, etc.)
- [dev/knowledge/doc-generation.md](dev/knowledge/doc-generation.md) - How docs are auto-generated from code

## Subdirectory guides

- [docs/AGENTS.md](docs/AGENTS.md) - Documentation (Docusaurus)
Expand Down
1 change: 1 addition & 0 deletions changelog/+artifact-composition.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `artifact_content`, `file_object_content`, `from_json`, and `from_yaml` Jinja2 filters for artifact content composition in templates.
1 change: 1 addition & 0 deletions changelog/+artifact-composition.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Replace `FilterDefinition.trusted: bool` with flag-based `ExecutionContext` model (`CORE`, `WORKER`, `LOCAL`) for context-aware template validation. `validate()` now accepts an optional `context` parameter. Backward compatible.
1 change: 1 addition & 0 deletions changelog/+infp380.removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removed the deprecated `raise_for_error` argument from `execute_graphql`, `query_gql_query`, `get_diff_summary`, `allocate_next_ip_address`, and `allocate_next_ip_prefix` client methods. HTTP errors are now always raised via `resp.raise_for_status()`.
1 change: 1 addition & 0 deletions changelog/654.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow direct assignment of authentication method to the configuration to override settings from environment variables.
1 change: 1 addition & 0 deletions changelog/958.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed `ObjectStore.get()` and `ObjectStore.upload()` silently swallowing non-2xx HTTP errors instead of raising them.
2 changes: 1 addition & 1 deletion dev/commands/speckit.specify.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Given that feature description, do this:
b. Find the highest feature number across all sources for the short-name:
- Remote branches: `git ls-remote --heads origin | grep -E 'refs/heads/[0-9]+-<short-name>$'`
- Local branches: `git branch | grep -E '^[* ]*[0-9]+-<short-name>$'`
- Specs directories: Check for directories matching `specs/[0-9]+-<short-name>`
- Specs directories: Check for directories matching `dev/specs/[0-9]+-<short-name>`

c. Determine the next available number:
- Extract all numbers from all three sources
Expand Down
51 changes: 51 additions & 0 deletions dev/knowledge/cli-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# CLI Architecture

The `infrahubctl` CLI is built with [Typer](https://typer.tiangolo.com/) via a custom `AsyncTyper` subclass that supports async command functions.

## Entry point

The main Typer app lives in `infrahub_sdk/ctl/cli_commands.py`. It is re-exported through `infrahub_sdk/ctl/cli.py` which adds the `infrahubctl` entry point name.

## Command hierarchy

Commands are organized in two tiers:

- **Root commands** are registered directly on the main app with `app.command()`. These are standalone operations that don't belong to a logical group (e.g. `dump`, `load`, `check`, `render`, `run`, `transform`, `protocols`, `version`, `info`).
- **Subcommand groups** are separate `AsyncTyper()` instances registered with `app.add_typer(sub_app, name="group")`. Each group lives in its own module under `infrahub_sdk/ctl/`. Current groups: `branch`, `schema`, `validate`, `repository`, `menu`, `object`, `graphql`, `task`.

## Adding a new command

For a **root command**, define the function in the appropriate module and register it in `cli_commands.py`:

```python
app.command(name="mycommand")(my_function)
```

For a **subcommand**, add it to the relevant group's package or module. For example, object subcommands live in `infrahub_sdk/ctl/object/` and are registered on the object app in `__init__.py`.

## Group packages

When a subcommand group has multiple commands, it lives as a package (directory with `__init__.py`) rather than a single module file. The `object` group is the reference example:

```text
infrahub_sdk/ctl/object/
├── __init__.py # App, callback, load/validate commands, registers CRUD
├── create.py # create subcommand
├── delete.py # delete subcommand
├── get.py # get subcommand
├── update.py # update subcommand
└── utils.py # Shared utilities (resolve_node, etc.)
```

Each command file contains a single command function. Shared logic goes in `utils.py`. The `__init__.py` wires everything together by importing and registering commands on the group's `AsyncTyper` app. Other groups that grow beyond a single file should follow this same pattern.

## Decorators

- `@catch_exception(console=console)` wraps commands for consistent error handling via Rich.
- Async commands work natively thanks to `AsyncTyper`.

## Design rules

**Always check if a new command belongs in an existing group before adding it at the root.** A command that operates on a specific resource type (objects, branches, schemas, etc.) should go under the matching subgroup, not at the top level. Root-level commands are reserved for cross-cutting or standalone operations (e.g. `run`, `version`, `info`).

When in doubt, look at what the command acts on and find the group that matches. For example, anything that creates, reads, updates, or deletes Infrahub objects belongs under `object`, not at the root.
45 changes: 45 additions & 0 deletions dev/knowledge/cli-design-principles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# CLI Design Principles

Guidelines for writing `infrahubctl` commands. These complement the structural rules in `cli-architecture.md`.

## Don't pre-validate what the server validates

The CLI's job is to translate user intent into API calls, not to reimplement server-side logic. When in doubt, just fire the request and let the server respond.

**Avoid**:

- Extra round-trips to check whether an object already exists before creating it
- Re-implementing uniqueness, permission, or referential-integrity checks client-side
- Heuristics that guess at server behavior (e.g., deriving identifiers from partial data to simulate a lookup)

**Why this matters**:

- Pre-validation is stale by the time the real call is made (TOCTOU).
- The server returns better errors than the CLI can construct.
- Every client-side check is a duplication that will drift from the server implementation.
- Extra calls slow the CLI down and make it feel laggy on high-latency links.

**Exception**: local-only validation that doesn't need the server is fine — malformed `--set` arguments, mutually exclusive flags, missing required options, etc. The test is: "could the server figure this out from the request alone?" If yes, let the server do it.

## Prefer HFID over `default_filter`

When resolving identifiers, HFID (human-friendly ID) is the long-term path. `default_filter` is deprecated and will be removed. New code should not depend on it, and existing code should be written so its removal is a straightforward change.

## Output phrasing should match semantics

Messages like `Created`, `Updated`, `Deleted` are promises about what actually happened. When using upsert semantics (`allow_upsert=True`), the CLI genuinely doesn't know whether the object was created or updated — so use neutral phrasing like `Saved` or `Applied`. Don't lie to the user for the sake of a cleaner message.

## Deduplicate redundant output

When printing an object's label and ID, check that they're actually different before printing both. Many objects have `display_label == id`, in which case repeating the value adds noise:

```python
if node.display_label and node.display_label != node.id:
console.print(f"Saved {kind} '{node.display_label}' (ID: {node.id})")
else:
console.print(f"Saved {kind} (ID: {node.id})")
```

## Raise errors directly, don't invoke dead calls

If the CLI has determined a call will fail (e.g., no lookup strategy matched), raise the appropriate exception directly rather than making a request you know will error out just to piggyback on the server's error message. The request is wasted network traffic and the roundabout path is harder to read.
40 changes: 40 additions & 0 deletions dev/knowledge/doc-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Documentation Generation

CLI and SDK documentation is auto-generated from code. Always regenerate after changing commands, config, or public docstrings.

## How to run

```bash
uv run invoke docs-generate # Generate all docs (CLI + SDK)
uv run invoke docs-validate # Verify generated docs match committed versions
```

## CLI documentation

Defined in `tasks.py` (`_generate_infrahubctl_documentation`). The process:

1. Deletes all existing `infrahubctl-*.mdx` files in `docs/docs/infrahubctl/`.
2. Iterates `app.registered_commands` and creates a `TyperSingleCommand` for each.
3. Iterates `app.registered_groups` and creates a `TyperGroupCommand` for each.
4. Each command object generates an mdx file via `typer ... utils docs`.

### How it maps to files

- A **root command** named `foo` produces `infrahubctl-foo.mdx` using:
`uv run typer --func foo infrahub_sdk.ctl.cli_commands utils docs --name "infrahubctl foo"`
- A **subcommand group** named `bar` produces `infrahubctl-bar.mdx` using:
`uv run typer infrahub_sdk.ctl.bar utils docs --name "infrahubctl bar"`

The group variant documents all subcommands within that group automatically.

### Key implication

Moving a command from root to a group (or vice versa) changes which mdx files get generated. The old files are cleaned up automatically by the glob delete, but the new ones only appear after running `docs-generate`. Always regenerate and commit the result.

## SDK documentation

Other doc generators cover SDK config, compatibility matrix, templates, and API reference. These are independent of CLI structure and are also triggered by `docs-generate`.

## Validation in CI

`docs-validate` diffs the generated output against the committed files. If they don't match, CI fails. This ensures docs stay in sync with code.
36 changes: 36 additions & 0 deletions dev/specs/001-end-user-cli/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Specification Quality Checklist: End-User CLI (`infrahubctl` command)

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-03-28
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- All items pass validation. Spec is ready for `/speckit.clarify` or `/speckit.plan`.
- Assumptions section documents reasonable defaults for unspecified details.
- CLI command examples in acceptance scenarios use generic syntax (not framework-specific).
64 changes: 64 additions & 0 deletions dev/specs/001-end-user-cli/contracts/cli-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# CLI Command Contracts

## Global Options

All commands accept:

- `--branch TEXT` — Target Infrahub branch (default: from config)
- `--config-file PATH` — Configuration file path (default: infrahubctl.toml)
- `--output [table|json|csv|yaml]` — Output format (default: table if TTY, json if piped)

## `infrahubctl get <kind> [identifier]`

**List mode** (no identifier):

- Input: kind (positional), --filter (repeatable), --limit INT, --offset INT
- Output: Table with columns for each attribute + relationship (display names)
- Exit 0: results found | Exit 80: no results (empty list) | Exit 1: invalid kind

**Detail mode** (with identifier):

- Input: kind (positional), identifier (positional — UUID or display name)
- Output: Key-value display of all attributes, relationships, metadata
- Exit 0: found | Exit 1: not found

**Filters**: `--filter name__value="spine01"` (repeatable)

## `infrahubctl create <kind>`

- Input: kind (positional), --set key=value (repeatable), --file PATH
- --set and --file are mutually exclusive
- Output: Confirmation with created object ID and display label
- Exit 0: created | Exit 1: validation error | Exit 1: server error

**File input**: JSON or YAML in Infrahub Object format
(`apiVersion: infrahub.app/v1`)

## `infrahubctl update <kind> <identifier>`

- Input: kind (positional), identifier (positional), --set key=value
(repeatable), --file PATH
- --set and --file are mutually exclusive
- Output: Confirmation with old → new values for changed fields
- Exit 0: updated | Exit 1: not found | Exit 1: validation error

## `infrahubctl delete <kind> <identifier>`

- Input: kind (positional), identifier (positional), --yes (skip confirmation)
- Output: Confirmation prompt (unless --yes), then success message
- Exit 0: deleted | Exit 1: not found | Exit 1: dependency conflict

## `infrahubctl schema list`

- Input: --filter TEXT (substring match on kind name)
- Output: Table with columns: Namespace, Name, Kind, Description
- Exit 0: always (empty table if no matches)

## `infrahubctl schema show <kind>`

- Input: kind (positional)
- Output: Formatted display of:
- Kind metadata (namespace, label, description, display_labels, HFID)
- Attributes table (name, type, required, default, description)
- Relationships table (name, peer kind, cardinality, optional)
- Exit 0: found | Exit 1: invalid kind
Loading
Loading