Skip to content

feat(describe): bcli describe --format json (AIP Phase 1)#14

Merged
igor-ctrl merged 1 commit into
mainfrom
feat/describe-cmd
May 17, 2026
Merged

feat(describe): bcli describe --format json (AIP Phase 1)#14
igor-ctrl merged 1 commit into
mainfrom
feat/describe-cmd

Conversation

@igor-ctrl
Copy link
Copy Markdown
Owner

Implements AIP v0.1 Phase 1 per agent-cli-contract-v0.1.md.

Summary

  • New bcli describe [--format json|table] [<command-path>...] command
  • Projects live Typer app + EndpointRegistry + active BCProfile as one JSON document — the canonical artifact MCP, completions, and docs all consume (no parallel schemas drifting from the CLI).
  • Caches at ~/.config/bcli/describe/<profile>.<hash>.json with registry/profile mtime invalidation; writes are atomic (.tmp + os.replace).
  • Subtree mode for token-constrained agents (bcli describe get, bcli describe batch run) — drops registry + profile_constraints, keeps version/tool/tool_version/profile for provenance.
  • Forward-compat declarations:
    • emits_result_envelope: true on mutating commands (post/patch/delete/attach upload/batch run) — Phase 2 attaches here.
    • emits_operation_state: true only on batch run — Phase 3.
    • requires_confirmation: "production" on mutating commands.
  • Tolerates broken/missing config: when no profile is configured, command still emits a populated commands list with profile: null — matches the self-rescue posture of bcli doctor. Agents calling describe as their first command always get useful output.
  • Stdlib only, no new runtime deps. Additive only — zero user-visible change to existing commands.

Design decisions worth flagging

  • --format is a local flag on describe (not the root --format). Spec example calls it out and Typer happily shadows the root option locally.
  • Subtree responses drop registry and profile_constraints deliberately — token savings is the whole point. State is documented in the command docstring.
  • Commands sorted alphabetically by path tuple, endpoints by (tier, entity) — deterministic output for diffing.
  • fields_discovered_at is included in the per-endpoint shape but emitted as null until EndpointMetadata is extended (deferred — out of scope for this PR).

Test plan

  • uv run pytest tests/test_describe/ -v — 19 tests, all green
  • uv run pytest tests/ -v — 650 passed, 5 skipped, no regressions
  • uv run ruff check src/ tests/ — clean
  • Manual: bcli describe --format json — 46 commands + 122 registry endpoints projected on real profile
  • Manual: bcli describe get --format json — subtree returns single command, drops registry/constraints
  • Manual: bcli describe --format table — Rich table with effects + formats columns

Implements the canonical agent-facing surface introspection per
agent-cli-contract-v0.1.md §Phase 1.

- New `bcli describe [--format json|table] [<command-path>...]` command
- Projects live Typer app + EndpointRegistry + active BCProfile as one
  JSON document — the single source of truth that MCP, completions, and
  docs will consume (no parallel schemas).
- Caches at ~/.config/bcli/describe/<profile>.<hash>.json with
  registry/profile mtime invalidation; writes are atomic.
- Subtree mode (`bcli describe get`, `bcli describe batch run`) trims
  the payload to one command for token-constrained agents — drops
  registry + profile_constraints, keeps version/tool/tool_version/profile
  for provenance.
- Forward-compat declarations:
    * `emits_result_envelope: true` on mutating commands (post/patch/
      delete/attach upload/batch run) — Phase 2 attaches to this.
    * `emits_operation_state: true` only on `batch run` — Phase 3.
    * `requires_confirmation: "production"` on mutating commands.
- Tolerates broken/missing config: when no profile is configured the
  command still emits a populated commands list with `profile: null`,
  matching the self-rescue posture of `bcli doctor`.
- Additive only — zero behavior change for existing commands.
- Stdlib only, no new runtime deps.
Copy link
Copy Markdown
Owner Author

@igor-ctrl igor-ctrl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lead review — APPROVE (pending Igor's merge)

Reviewed against agent-cli-contract-v0.1.md §Phase 1 and tasks/todo.md Phase 1 checklist. Worker self-report verified: 650 passed / 5 skipped, ruff clean, additive only, stdlib only.

Schema fidelity

  • All top-level keys (version, tool, tool_version, profile, commands, registry, profile_constraints) match the spec example.
  • commands[].options[].validates: "odata-filter" on --filter — matches the spec sample.
  • commands[].effects taxonomy (read / mutating / other) — other is an addition beyond the spec example but a sensible default for auth login, config set, etc.; doesn't break the contract.
  • requires_confirmation: "production" emitted on every mutating verb (spec example only showed it on batch run; emitting it on all mutating verbs is the conservative reading and aligned with the existing _safety.py posture).
  • emits_result_envelope: true on the five mutating paths; emits_operation_state: true on batch run only — exactly the forward-compat declarations Phase 2 / Phase 3 attach to.
  • registry.endpoints[] shape matches; fields_discovered_at: null (deferred; flagged in PR body — additive extension when EndpointMetadata gains the field).

Implementation quality

  • Stdlib only, no new runtime deps.
  • Atomic cache write (tempfile.mkstemp + os.replace with exception cleanup) — Phase 2 envelope can reuse this pattern.
  • Lazy imports for CONFIG_DIR / app — correctly motivated for test monkeypatching and circular-import avoidance; docstring explains it.
  • Deterministic ordering (commands by path tuple, endpoints by (tier, entity)) — important for diffing describe outputs across runs; tested.
  • Broken-install tolerance: nested try/except in _build_payload falls back to a stub registry projection. Matches the self-rescue posture of bcli doctor. This is the right call for an agent's first command.

Test coverage (19 new tests)

Schema shape, command list invariants, self-reference, sort determinism, mutating flags, no-leak of forward-compat flags onto read commands, batch run operation_state, registry projection with custom endpoints, disable_standard_api reflection, profile constraints (set + default), no-profile tolerance, subtree single match, multipart subtree, unknown-path error path, cache write, cache hit (sentinel proves cache is consulted), cache invalidation on registry mtime, table format. Hits every Phase 1 checklist box.

Live smoke (in tmp worktree)

bcli describe --format json    → 46 commands, 122 endpoints, profile resolved
bcli describe get              → 1 command, no registry/constraints (token savings)
bcli describe batch run        → emits_result_envelope=True, emits_operation_state=True, requires_confirmation=production

Non-blocking observations

  1. _supported_formats_from_signature parses help text by splitting on :/, — brittle but harmlessly so (falls back to ["json"]; existing help strings are consistent).
  2. _annotation_to_name does string munging on annotations — works for current signatures; list[str]/Union[...] would produce lowercased fragments but won't crash.
  3. typer.Exit(4) on unknown subtree path — aligns with the (future) Phase 4 exit-code taxonomy where 4 = not found.

After this merges

Branch feat/standardize-cli off main and proceed with Phase 4 per tasks/todo.md.

Verdict: APPROVE. Igor merges; I do not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant