netbox-super-cli 1.0.0__tar.gz → 1.0.2__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.
- netbox_super_cli-1.0.2/.github/workflows/claude.yml +50 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.gitignore +3 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/AGENTS.md +15 -4
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/CHANGELOG.md +30 -1
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/CLAUDE.md +15 -4
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/LICENSE +10 -9
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/PKG-INFO +6 -6
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/README.md +5 -5
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/architecture/caching.md +1 -1
- netbox_super_cli-1.0.2/docs/architecture/schema-loading.md +84 -0
- netbox_super_cli-1.0.2/docs/contributing/branching.md +110 -0
- netbox_super_cli-1.0.2/docs/contributing/release-process.md +94 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/getting-started/install.md +0 -2
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/index.md +0 -2
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/reference/config.md +2 -2
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/mkdocs.yml +1 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/_version.py +1 -1
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cache/store.py +45 -1
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/app.py +13 -1
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/commands_dump.py +13 -1
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/globals.py +5 -1
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/init_commands.py +21 -11
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/runtime.py +30 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/config/models.py +1 -1
- netbox_super_cli-1.0.2/nsc/schema/source.py +208 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/pyproject.toml +1 -1
- netbox_super_cli-1.0.2/skills/netbox-super-cli/SKILL.md +291 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cache/test_store.py +39 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_app_smoke.py +53 -0
- netbox_super_cli-1.0.2/tests/cli/test_commands_dump.py +121 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_init_commands.py +49 -4
- netbox_super_cli-1.0.2/tests/cli/test_meta_subcommands_under_broken_config.py +93 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_runtime.py +48 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/config/test_models.py +1 -1
- netbox_super_cli-1.0.2/tests/schema/test_source_ttl.py +361 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/uv.lock +1 -1
- netbox_super_cli-1.0.0/docs/architecture/schema-loading.md +0 -46
- netbox_super_cli-1.0.0/docs/contributing/release-process.md +0 -42
- netbox_super_cli-1.0.0/nsc/schema/source.py +0 -120
- netbox_super_cli-1.0.0/skills/netbox-super-cli/SKILL.md +0 -127
- netbox_super_cli-1.0.0/tests/cli/test_commands_dump.py +0 -44
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.github/workflows/agents-md-sync.yml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.github/workflows/bench.yml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.github/workflows/docs.yml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.github/workflows/e2e.yml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.github/workflows/lint.yml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.github/workflows/release.yml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.github/workflows/test.yml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.pre-commit-config.yaml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/.python-version +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/architecture/command-generation.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/architecture/http-client.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/architecture/overview.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/contributing/adding-bundled-schemas.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/contributing/development.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/getting-started/concepts.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/getting-started/first-run.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/guides/ci-and-automation.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/guides/managing-profiles.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/guides/output-formats.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/guides/using-with-ai-agents.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/guides/working-with-plugins.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/guides/writes-and-safety.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/reference/cli.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/reference/exit-codes.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/docs/reference/schemas.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/justfile +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/__main__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/aliases/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/aliases/resolver.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/auth/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/auth/verify.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/builder/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/builder/build.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cache/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/aliases_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/cache_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/config_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/handlers.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/login_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/profiles_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/registration.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/skill_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/writes/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/writes/apply.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/writes/bulk.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/writes/coercion.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/writes/confirmation.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/writes/input.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/cli/writes/preflight.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/config/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/config/loader.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/config/settings.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/config/writer.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/http/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/http/audit.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/http/client.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/http/errors.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/http/retry.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/model/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/model/command_model.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/csv_.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/errors.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/explain.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/flatten.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/headers.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/json_.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/jsonl.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/render.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/table.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/output/yaml_.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/schema/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/schema/hashing.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/schema/loader.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/schema/models.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/schemas/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/schemas/bundled/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/schemas/bundled/manifest.yaml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/schemas/bundled/netbox-4.6.0-beta2.json.gz +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/nsc/skill/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/scripts/gen_docs.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/scripts/sync_agents_md.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/aliases/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/aliases/test_resolver.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/auth/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/auth/test_verify.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/benchmarks/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/benchmarks/test_startup.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/builder/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/builder/test_build.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/builder/test_default_columns.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/builder/test_redaction_marking.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/builder/test_request_body_shape.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cache/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cache/test_prune.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_aliases_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_cache_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_completion_smoke.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_config_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_explain.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_handlers.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_login_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_ndjson_input.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_profiles_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_registration.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_respx_integration.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_skill_commands.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_stdin_sniffer.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/test_writes_respx.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/writes/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/writes/test_apply.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/writes/test_bulk.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/writes/test_confirmation.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/writes/test_handler_helpers.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/writes/test_handlers_audit.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/writes/test_input.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/cli/writes/test_preflight.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/config/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/config/test_loader.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/config/test_writer_atomicity.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/config/test_writer_dotted_paths.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/config/test_writer_roundtrip.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/conftest.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/README.md +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/conftest.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/docker-compose.yml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/test_audit_redaction.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/test_auth_error_envelope.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/test_bulk_create_with_loop_fallback.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/test_full_cycle.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/test_login.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/test_ndjson.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/test_preflight_blocks_apply.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/test_validation_error_envelope_from_netbox.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/e2e/wait_for_netbox.sh +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/fixtures/profiles/single_profile.yaml +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/fixtures/responses/auth_401.json +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/fixtures/responses/circuits_providers_list.json +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/fixtures/responses/dcim_devices_get.json +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/fixtures/responses/dcim_devices_list_p1.json +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/fixtures/responses/dcim_devices_list_p2.json +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/http/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/http/test_audit.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/http/test_audit_redaction.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/http/test_client.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/http/test_client_redaction_threading.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/http/test_errors.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/http/test_retry.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/model/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/model/test_command_model.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/model/test_request_body_shape.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_csv.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_errors.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_errors_aliases.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_explain.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_flatten.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_input_error_envelope.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_json.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_jsonl.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_render_dispatch.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_table.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/output/test_yaml.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/schema/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/schema/test_hashing.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/schema/test_loader.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/schema/test_models.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/schema/test_source.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/scripts/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/scripts/test_gen_docs.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/skill/__init__.py +0 -0
- {netbox_super_cli-1.0.0 → netbox_super_cli-1.0.2}/tests/skill/test_bundle.py +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Claude Code
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment:
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request_review_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
issues:
|
|
9
|
+
types: [opened, assigned]
|
|
10
|
+
pull_request_review:
|
|
11
|
+
types: [submitted]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude:
|
|
15
|
+
if: |
|
|
16
|
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
17
|
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
18
|
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
19
|
+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
pull-requests: read
|
|
24
|
+
issues: read
|
|
25
|
+
id-token: write
|
|
26
|
+
actions: read # Required for Claude to read CI results on PRs
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout repository
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
with:
|
|
31
|
+
fetch-depth: 1
|
|
32
|
+
|
|
33
|
+
- name: Run Claude Code
|
|
34
|
+
id: claude
|
|
35
|
+
uses: anthropics/claude-code-action@v1
|
|
36
|
+
with:
|
|
37
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
38
|
+
|
|
39
|
+
# This is an optional setting that allows Claude to read CI results on PRs
|
|
40
|
+
additional_permissions: |
|
|
41
|
+
actions: read
|
|
42
|
+
|
|
43
|
+
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
|
44
|
+
# prompt: 'Update the pull request description to include a summary of changes.'
|
|
45
|
+
|
|
46
|
+
# Optional: Add claude_args to customize behavior and configuration
|
|
47
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
49
|
+
# claude_args: '--allowed-tools Bash(gh pr *)'
|
|
50
|
+
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
# Working on netbox-super-cli (for AI agents)
|
|
9
9
|
|
|
10
|
-
This is the contributor guide for AI agents (and humans!) modifying this repo. The end-user-facing AI guide is the bundled Skill at `skills/netbox-super-cli/SKILL.md
|
|
10
|
+
This is the contributor guide for AI agents (and humans!) modifying this repo. The end-user-facing AI guide is the bundled Skill at `skills/netbox-super-cli/SKILL.md`.
|
|
11
11
|
|
|
12
12
|
## Architecture cheat sheet
|
|
13
13
|
|
|
@@ -15,10 +15,13 @@ This is the contributor guide for AI agents (and humans!) modifying this repo. T
|
|
|
15
15
|
- `nsc/model/` — the normalized command tree (data only, framework-free). The "brain".
|
|
16
16
|
- `nsc/builder/` — converts a parsed schema into a `CommandModel`.
|
|
17
17
|
- `nsc/cli/` — the Typer app; consumes a `CommandModel`.
|
|
18
|
-
- `nsc/http/` — thin httpx wrapper
|
|
19
|
-
- `nsc/output/` — formatters (
|
|
20
|
-
- `nsc/config/` — config loader + Pydantic models
|
|
18
|
+
- `nsc/http/` — thin httpx wrapper: auth, retries, audit log.
|
|
19
|
+
- `nsc/output/` — formatters (table/json/jsonl/yaml/csv) + error envelope.
|
|
20
|
+
- `nsc/config/` — config loader + Pydantic models + ruamel.yaml round-trip writer.
|
|
21
21
|
- `nsc/cache/` — disk cache for generated command-models.
|
|
22
|
+
- `nsc/auth/` — login verification helpers (pre-flight probes, token rotate).
|
|
23
|
+
- `nsc/aliases/` — curated alias resolver (`ls`, `get`, `rm`, `search`). Framework-free.
|
|
24
|
+
- `nsc/skill/` — bundle-path helper for the portable `SKILL.md` shipped in the wheel.
|
|
22
25
|
- `nsc/schemas/bundled/` — versioned NetBox OpenAPI snapshots, fallback when offline.
|
|
23
26
|
|
|
24
27
|
The hard rule: **`nsc/model/` imports nothing from `nsc/cli/`, `nsc/http/`, or any framework.** If you need to add a dependency to `model/`, you're probably solving the wrong problem.
|
|
@@ -39,6 +42,14 @@ The hard rule: **`nsc/model/` imports nothing from `nsc/cli/`, `nsc/http/`, or a
|
|
|
39
42
|
- TDD: write the failing test first.
|
|
40
43
|
- No comments on what code does; only on non-obvious *why*.
|
|
41
44
|
|
|
45
|
+
## Branching
|
|
46
|
+
|
|
47
|
+
`main` is protected — direct pushes are rejected. All work happens on
|
|
48
|
+
short-lived feature branches (`fix/<slug>`, `feat/<slug>`, `docs/<slug>`,
|
|
49
|
+
…), opens a PR against `main`, passes required CI checks, and squash-merges.
|
|
50
|
+
Releases are *tags* on `main`, not branches. Full convention:
|
|
51
|
+
[`docs/contributing/branching.md`](docs/contributing/branching.md).
|
|
52
|
+
|
|
42
53
|
## Where the design lives
|
|
43
54
|
|
|
44
55
|
- `docs/superpowers/specs/2026-04-30-netbox-super-cli-design.md` — the full design.
|
|
@@ -2,7 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to netbox-super-cli are tracked here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loosely. From v1.0.0 onward, releases follow [Semantic Versioning](https://semver.org/) and the version in `pyproject.toml` matches the git tag. Pre-1.0 milestones (Phase 1-5) were pinned by tag while `pyproject.toml` stayed at `0.0.1`.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## v1.0.2 — 2026-05-07
|
|
6
|
+
|
|
7
|
+
Second patch release. Headline change is the schema TTL fast-path (issue #34), which eliminates the per-invocation `GET /api/schema/` round-trip on warm caches. Also includes a small `nsc init` UX addition (verify_ssl prompt) and the documentation pass that landed since v1.0.1.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Schema TTL fast-path** (issue #34). `nsc` now skips the per-invocation `GET /api/schema/` round-trip when a sidecar-validated cache entry is fresh. Freshness is tracked in `~/.nsc/cache/<profile>/<hash>.meta.json` (an explicit `fetched_at` timestamp), not file mtime — so `touch`, backup-restore, or `cp -p` can't fake freshness, and clocks that jumped forward then back are rejected (>60s skew distrusted).
|
|
12
|
+
- **`nsc --refresh-schema <subcmd>`** — one-shot forced refresh that bypasses the TTL even on `daily`/`weekly`/`manual` policies. Use after a NetBox upgrade to pick up a new schema immediately without changing config.
|
|
13
|
+
- Atomic cache writes — `CacheStore.save` now writes via temp-file + `os.replace`, so a concurrent `nsc` reader can never observe a partial cache file.
|
|
14
|
+
- **`nsc init` prompts for SSL verification** (issue #22). The first-run wizard now asks `Verify SSL certificates?` (defaults `Y`) after URL entry. Accepting the default keeps the config minimal (no `verify_ssl` key written); answering `n` writes `verify_ssl: false` into the profile block.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Default `defaults.schema_refresh` is now `daily`** (was `on-hash-change`). This is the user-visible behaviour change behind the fast-path: under the new default, `nsc` trusts a cached schema for 24h. **If you need the v1.0.1 behaviour** (re-fetch every invocation to detect a schema change immediately), set `defaults.schema_refresh: on-hash-change` in `~/.nsc/config.yaml`. To force a one-shot refresh under any policy, prepend `--refresh-schema` to the command. Existing configs with an explicit `schema_refresh` value are unaffected.
|
|
19
|
+
- Cache files written before this release have no sidecar and will be treated as stale on first invocation after upgrade — `nsc` refetches once, writes the sidecar, and the fast-path is active from there.
|
|
20
|
+
|
|
21
|
+
### Documentation
|
|
22
|
+
|
|
23
|
+
- LICENSE file normalized byte-for-byte to the apache.org canonical Apache 2.0 text (issue #18).
|
|
24
|
+
- Trunk-based branching guide added at `docs/contributing/branching.md`; branch protection enabled on `main` (PR #28).
|
|
25
|
+
- Bundled Skill (`skills/netbox-super-cli/SKILL.md`) gained a NetBox device type library section (PR #27).
|
|
26
|
+
|
|
27
|
+
## v1.0.1 — 2026-05-06
|
|
28
|
+
|
|
29
|
+
First patch release. Two bug fixes; no API or behavior changes elsewhere.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **`nsc commands --schema <https-url>` now honours `--insecure`, `NSC_INSECURE`, and the active profile's `verify_ssl: false`** (issue #8). The `commands` meta-command bypasses the bootstrap pipeline that resolves the SSL flag, so it had been calling `httpx.get` with `verify=True` regardless of the user's configuration. The handler now reads the global state and a new `resolve_transport_settings` helper computes `(verify_ssl, timeout)` without requiring URL/token (which `commands` does not need).
|
|
34
|
+
- **`nsc commands` and `nsc config <…>` now run when `~/.nsc/config.yaml` fails to parse** (issue #10). The `ConfigParseError` recovery branch in `nsc/cli/app.py:_root` listed only 5 of the 7 meta subcommands — `commands` and `config` were missing, so running them against a malformed config aborted with the unhelpful root-level `Error: <ConfigParseError>` instead of recovering to an empty `Config()`. The recovery set is now sourced from `_META_COMMANDS` directly (single source of truth).
|
|
6
35
|
|
|
7
36
|
## v1.0.0 — 2026-05-06
|
|
8
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Working on netbox-super-cli (for AI agents)
|
|
2
2
|
|
|
3
|
-
This is the contributor guide for AI agents (and humans!) modifying this repo. The end-user-facing AI guide is the bundled Skill at `skills/netbox-super-cli/SKILL.md
|
|
3
|
+
This is the contributor guide for AI agents (and humans!) modifying this repo. The end-user-facing AI guide is the bundled Skill at `skills/netbox-super-cli/SKILL.md`.
|
|
4
4
|
|
|
5
5
|
## Architecture cheat sheet
|
|
6
6
|
|
|
@@ -8,10 +8,13 @@ This is the contributor guide for AI agents (and humans!) modifying this repo. T
|
|
|
8
8
|
- `nsc/model/` — the normalized command tree (data only, framework-free). The "brain".
|
|
9
9
|
- `nsc/builder/` — converts a parsed schema into a `CommandModel`.
|
|
10
10
|
- `nsc/cli/` — the Typer app; consumes a `CommandModel`.
|
|
11
|
-
- `nsc/http/` — thin httpx wrapper
|
|
12
|
-
- `nsc/output/` — formatters (
|
|
13
|
-
- `nsc/config/` — config loader + Pydantic models
|
|
11
|
+
- `nsc/http/` — thin httpx wrapper: auth, retries, audit log.
|
|
12
|
+
- `nsc/output/` — formatters (table/json/jsonl/yaml/csv) + error envelope.
|
|
13
|
+
- `nsc/config/` — config loader + Pydantic models + ruamel.yaml round-trip writer.
|
|
14
14
|
- `nsc/cache/` — disk cache for generated command-models.
|
|
15
|
+
- `nsc/auth/` — login verification helpers (pre-flight probes, token rotate).
|
|
16
|
+
- `nsc/aliases/` — curated alias resolver (`ls`, `get`, `rm`, `search`). Framework-free.
|
|
17
|
+
- `nsc/skill/` — bundle-path helper for the portable `SKILL.md` shipped in the wheel.
|
|
15
18
|
- `nsc/schemas/bundled/` — versioned NetBox OpenAPI snapshots, fallback when offline.
|
|
16
19
|
|
|
17
20
|
The hard rule: **`nsc/model/` imports nothing from `nsc/cli/`, `nsc/http/`, or any framework.** If you need to add a dependency to `model/`, you're probably solving the wrong problem.
|
|
@@ -32,6 +35,14 @@ The hard rule: **`nsc/model/` imports nothing from `nsc/cli/`, `nsc/http/`, or a
|
|
|
32
35
|
- TDD: write the failing test first.
|
|
33
36
|
- No comments on what code does; only on non-obvious *why*.
|
|
34
37
|
|
|
38
|
+
## Branching
|
|
39
|
+
|
|
40
|
+
`main` is protected — direct pushes are rejected. All work happens on
|
|
41
|
+
short-lived feature branches (`fix/<slug>`, `feat/<slug>`, `docs/<slug>`,
|
|
42
|
+
…), opens a PR against `main`, passes required CI checks, and squash-merges.
|
|
43
|
+
Releases are *tags* on `main`, not branches. Full convention:
|
|
44
|
+
[`docs/contributing/branching.md`](docs/contributing/branching.md).
|
|
45
|
+
|
|
35
46
|
## Where the design lives
|
|
36
47
|
|
|
37
48
|
- `docs/superpowers/specs/2026-04-30-netbox-super-cli-design.md` — the full design.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
Apache License
|
|
2
3
|
Version 2.0, January 2004
|
|
3
4
|
http://www.apache.org/licenses/
|
|
@@ -32,10 +33,10 @@
|
|
|
32
33
|
not limited to compiled object code, generated documentation,
|
|
33
34
|
and conversions to other media types.
|
|
34
35
|
|
|
35
|
-
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
-
form, made available under the License, as indicated by a
|
|
37
|
-
notice that is included in or attached to the work
|
|
38
|
-
provided in the Appendix below).
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
39
40
|
|
|
40
41
|
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
42
|
form, that is based on (or derived from) the Work and for which the
|
|
@@ -155,7 +156,7 @@
|
|
|
155
156
|
unless required by applicable law (such as deliberate and grossly
|
|
156
157
|
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
158
|
liable to You for damages, including any direct, indirect, special,
|
|
158
|
-
incidental, or
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
159
160
|
result of this License or out of the use or inability to use the
|
|
160
161
|
Work (including but not limited to damages for loss of goodwill,
|
|
161
162
|
work stoppage, computer failure or malfunction, or any and all
|
|
@@ -163,12 +164,12 @@
|
|
|
163
164
|
has been advised of the possibility of such damages.
|
|
164
165
|
|
|
165
166
|
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
-
the Work or Derivative Works thereof,
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
168
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
169
|
or other liability obligations and/or rights consistent with this
|
|
169
|
-
License. However, in accepting such obligations,
|
|
170
|
-
on
|
|
171
|
-
of any other Contributor, and only if
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
173
|
defend, and hold each Contributor harmless for any liability
|
|
173
174
|
incurred by, or claims asserted against, such Contributor by reason
|
|
174
175
|
of your accepting any such warranty or additional liability.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-super-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Dynamic NetBox CLI generated from the live OpenAPI schema.
|
|
5
5
|
Project-URL: Homepage, https://github.com/thomaschristory/netbox-super-cli
|
|
6
6
|
Project-URL: Issues, https://github.com/thomaschristory/netbox-super-cli/issues
|
|
@@ -31,8 +31,6 @@ A Python CLI for [NetBox](https://netbox.dev/) that builds its command tree dyna
|
|
|
31
31
|
|
|
32
32
|
> **Docs:** [thomaschristory.github.io/netbox-super-cli](https://thomaschristory.github.io/netbox-super-cli/) — the full guide, including install, first-run, guides, and the auto-generated CLI/config/exit-code reference.
|
|
33
33
|
|
|
34
|
-
> **Status:** Phase 3 complete (`v0.3.0-phase3`). The full read + safe-write surface is shipped: dynamic command tree, dry-run-by-default writes with `--apply` to commit, `--explain` for resolved-request introspection, bulk-endpoint detection with loop fallback, stable error envelopes with locked exit codes, append-only audit log, and a live-NetBox e2e suite running in CI against `netboxcommunity/netbox:v4.5.9`. Not yet on PyPI.
|
|
35
|
-
|
|
36
34
|
## Why
|
|
37
35
|
|
|
38
36
|
- **Plugins just work.** If your install has plugins, their endpoints appear as commands automatically.
|
|
@@ -40,13 +38,15 @@ A Python CLI for [NetBox](https://netbox.dev/) that builds its command tree dyna
|
|
|
40
38
|
- **Safe by default.** POST/PATCH/PUT/DELETE preview as dry-runs unless you pass `--apply`.
|
|
41
39
|
- **Agent-friendly.** Deterministic command shape, machine-readable JSON output, stable error envelope with documented exit codes.
|
|
42
40
|
|
|
43
|
-
## Install
|
|
41
|
+
## Install
|
|
44
42
|
|
|
45
43
|
```
|
|
46
44
|
uv tool install netbox-super-cli
|
|
45
|
+
# or, with pipx:
|
|
46
|
+
pipx install netbox-super-cli
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Or from source:
|
|
50
50
|
```
|
|
51
51
|
git clone https://github.com/thomaschristory/netbox-super-cli
|
|
52
52
|
cd netbox-super-cli
|
|
@@ -114,7 +114,7 @@ Stdin is sniffed from the first 512 bytes (first non-whitespace byte plus a one-
|
|
|
114
114
|
- HTTP headers in the `SENSITIVE_HEADERS` set (e.g. `Authorization`, `X-API-Key`) — replaced with `"<redacted>"`.
|
|
115
115
|
- Request-body fields whose OpenAPI definition has `format: password` OR whose name (case-insensitive) is one of: `password`, `secret`, `token`, `api_key`, `apikey`, `private_key`, `passphrase`, `client_secret`. Nested fields and arrays of objects are walked recursively.
|
|
116
116
|
|
|
117
|
-
The wire body sent to NetBox is **not** redacted — only the audit log. A failed write still records the redacted body; redaction is irreversible. Treat `audit.jsonl` like a verbose application log: gate it behind your home-directory permissions and rotate / archive accordingly. A "redact everything" mode is
|
|
117
|
+
The wire body sent to NetBox is **not** redacted — only the audit log. A failed write still records the redacted body; redaction is irreversible. Treat `audit.jsonl` like a verbose application log: gate it behind your home-directory permissions and rotate / archive accordingly. A "redact everything" mode is on the post-v1.0 roadmap.
|
|
118
118
|
|
|
119
119
|
## Output and errors
|
|
120
120
|
|
|
@@ -4,8 +4,6 @@ A Python CLI for [NetBox](https://netbox.dev/) that builds its command tree dyna
|
|
|
4
4
|
|
|
5
5
|
> **Docs:** [thomaschristory.github.io/netbox-super-cli](https://thomaschristory.github.io/netbox-super-cli/) — the full guide, including install, first-run, guides, and the auto-generated CLI/config/exit-code reference.
|
|
6
6
|
|
|
7
|
-
> **Status:** Phase 3 complete (`v0.3.0-phase3`). The full read + safe-write surface is shipped: dynamic command tree, dry-run-by-default writes with `--apply` to commit, `--explain` for resolved-request introspection, bulk-endpoint detection with loop fallback, stable error envelopes with locked exit codes, append-only audit log, and a live-NetBox e2e suite running in CI against `netboxcommunity/netbox:v4.5.9`. Not yet on PyPI.
|
|
8
|
-
|
|
9
7
|
## Why
|
|
10
8
|
|
|
11
9
|
- **Plugins just work.** If your install has plugins, their endpoints appear as commands automatically.
|
|
@@ -13,13 +11,15 @@ A Python CLI for [NetBox](https://netbox.dev/) that builds its command tree dyna
|
|
|
13
11
|
- **Safe by default.** POST/PATCH/PUT/DELETE preview as dry-runs unless you pass `--apply`.
|
|
14
12
|
- **Agent-friendly.** Deterministic command shape, machine-readable JSON output, stable error envelope with documented exit codes.
|
|
15
13
|
|
|
16
|
-
## Install
|
|
14
|
+
## Install
|
|
17
15
|
|
|
18
16
|
```
|
|
19
17
|
uv tool install netbox-super-cli
|
|
18
|
+
# or, with pipx:
|
|
19
|
+
pipx install netbox-super-cli
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Or from source:
|
|
23
23
|
```
|
|
24
24
|
git clone https://github.com/thomaschristory/netbox-super-cli
|
|
25
25
|
cd netbox-super-cli
|
|
@@ -87,7 +87,7 @@ Stdin is sniffed from the first 512 bytes (first non-whitespace byte plus a one-
|
|
|
87
87
|
- HTTP headers in the `SENSITIVE_HEADERS` set (e.g. `Authorization`, `X-API-Key`) — replaced with `"<redacted>"`.
|
|
88
88
|
- Request-body fields whose OpenAPI definition has `format: password` OR whose name (case-insensitive) is one of: `password`, `secret`, `token`, `api_key`, `apikey`, `private_key`, `passphrase`, `client_secret`. Nested fields and arrays of objects are walked recursively.
|
|
89
89
|
|
|
90
|
-
The wire body sent to NetBox is **not** redacted — only the audit log. A failed write still records the redacted body; redaction is irreversible. Treat `audit.jsonl` like a verbose application log: gate it behind your home-directory permissions and rotate / archive accordingly. A "redact everything" mode is
|
|
90
|
+
The wire body sent to NetBox is **not** redacted — only the audit log. A failed write still records the redacted body; redaction is irreversible. Treat `audit.jsonl` like a verbose application log: gate it behind your home-directory permissions and rotate / archive accordingly. A "redact everything" mode is on the post-v1.0 roadmap.
|
|
91
91
|
|
|
92
92
|
## Output and errors
|
|
93
93
|
|
|
@@ -26,7 +26,7 @@ one-line "schema changed, regenerating…" notice on stderr.
|
|
|
26
26
|
|
|
27
27
|
## Cleaning up
|
|
28
28
|
|
|
29
|
-
`nsc cache prune`
|
|
29
|
+
`nsc cache prune` handles three classes of orphan:
|
|
30
30
|
|
|
31
31
|
1. Profile directories not in `~/.nsc/config.yaml` (e.g., a removed profile).
|
|
32
32
|
2. `<schema_hash>.json` files inside an active profile whose hash no longer
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Schema loading
|
|
2
|
+
|
|
3
|
+
`nsc/schema/` fetches and parses the OpenAPI document.
|
|
4
|
+
|
|
5
|
+
## Resolution order
|
|
6
|
+
|
|
7
|
+
Highest priority first, given the active `defaults.schema_refresh` policy
|
|
8
|
+
and an optional `--refresh-schema` override:
|
|
9
|
+
|
|
10
|
+
1. `--schema <path-or-url>` — one-shot override. Always wins; never
|
|
11
|
+
consults the TTL fast-path or the network.
|
|
12
|
+
2. **TTL fast-path.** If the policy is `daily`, `weekly`, or `manual` and
|
|
13
|
+
the newest sidecar-validated cache entry for the active profile is
|
|
14
|
+
fresher than the policy's TTL, `nsc` returns it directly with no HTTP
|
|
15
|
+
round-trip. Skipped when `--refresh-schema` is passed (or the policy
|
|
16
|
+
is `on-hash-change`).
|
|
17
|
+
3. Live fetch from the active profile's `schema_url` (explicit) or
|
|
18
|
+
`{profile.url}/api/schema/?format=json`. The fetched body is hashed;
|
|
19
|
+
if a cache file already exists at that hash it's loaded directly,
|
|
20
|
+
otherwise the command-model is rebuilt and saved.
|
|
21
|
+
4. On fetch failure: any cached entry for the profile (warns once on
|
|
22
|
+
stderr).
|
|
23
|
+
5. Bundled snapshot at `nsc/schemas/bundled/netbox-<closest-version>.json`
|
|
24
|
+
(shipped in the wheel) — last-resort offline fallback.
|
|
25
|
+
|
|
26
|
+
## Hashing
|
|
27
|
+
|
|
28
|
+
The cache key is `sha256` of the canonicalized schema body (JSON
|
|
29
|
+
re-serialized with sorted keys, no whitespace). Cosmetic changes to the
|
|
30
|
+
upstream schema (whitespace, key order) don't bust the cache, but any
|
|
31
|
+
semantic change (new endpoint, changed field) does.
|
|
32
|
+
|
|
33
|
+
## Cache layout
|
|
34
|
+
|
|
35
|
+
Each profile has a directory under `~/.nsc/cache/<profile>/` containing,
|
|
36
|
+
per schema hash:
|
|
37
|
+
|
|
38
|
+
- `<hash>.json` — the generated `CommandModel`. Loaded via
|
|
39
|
+
`CacheStore.load`, which re-verifies that the file's embedded
|
|
40
|
+
`schema_hash` matches the filename — so a tampered or copied JSON
|
|
41
|
+
file is rejected.
|
|
42
|
+
- `<hash>.meta.json` — a sidecar with `{"fetched_at": <epoch_seconds>}`.
|
|
43
|
+
Drives the TTL fast-path. Written atomically alongside the cache file
|
|
44
|
+
via temp-file + `os.replace`.
|
|
45
|
+
|
|
46
|
+
A sidecar dated more than 60s in the future (clock skew or tampering)
|
|
47
|
+
is rejected. A cache entry without its sidecar is treated as stale —
|
|
48
|
+
that's the upgrade path for caches written before sidecars existed.
|
|
49
|
+
|
|
50
|
+
## Offline behavior
|
|
51
|
+
|
|
52
|
+
- Live schema unreachable + cache present → use cache, warn once on
|
|
53
|
+
stderr.
|
|
54
|
+
- Live schema unreachable + no cache → fall back to the closest bundled
|
|
55
|
+
version, warn loudly.
|
|
56
|
+
- All such errors include a remediation hint pointing at
|
|
57
|
+
`--refresh-schema` for once the instance is reachable again.
|
|
58
|
+
|
|
59
|
+
## Refresh modes
|
|
60
|
+
|
|
61
|
+
In `~/.nsc/config.yaml`:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
defaults:
|
|
65
|
+
schema_refresh: daily # default since the v1.0.2 release line
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Value | TTL | Behaviour |
|
|
69
|
+
|------------------|--------|-----------------------------------------------------------------|
|
|
70
|
+
| `daily` | 24h | Default. Trust a fresh cache; re-fetch once a day. |
|
|
71
|
+
| `weekly` | 7d | Trust longer; suitable for stable NetBox deployments. |
|
|
72
|
+
| `manual` | ∞ | Never auto-invalidate. Use `--refresh-schema` to update. |
|
|
73
|
+
| `on-hash-change` | 0 | v1.0.1 behaviour: re-fetch every invocation, compare hashes. |
|
|
74
|
+
|
|
75
|
+
Use `nsc --refresh-schema <subcommand>` to force a one-shot refresh
|
|
76
|
+
that bypasses the TTL fast-path under any policy.
|
|
77
|
+
|
|
78
|
+
## When you change NetBox versions
|
|
79
|
+
|
|
80
|
+
The hash changes → next invocation that bypasses the fast-path
|
|
81
|
+
regenerates the model. Under `daily` (the default) you may need to
|
|
82
|
+
prepend `--refresh-schema` to your first post-upgrade invocation, or
|
|
83
|
+
wait up to 24h. The old `<old_hash>.json` (and its sidecar) become
|
|
84
|
+
orphaned and get cleaned by `nsc cache prune`.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Branching and merging
|
|
2
|
+
|
|
3
|
+
`netbox-super-cli` follows trunk-based development with short-lived
|
|
4
|
+
feature branches. There is exactly one long-lived branch — `main` —
|
|
5
|
+
and it is always releasable.
|
|
6
|
+
|
|
7
|
+
## The branches
|
|
8
|
+
|
|
9
|
+
- **`main`** — the integration branch. Every accepted change lands here.
|
|
10
|
+
Each commit on `main` is a candidate for the next release.
|
|
11
|
+
- **Feature branches** — short-lived, named for the change they carry.
|
|
12
|
+
Created from `main`, deleted on merge.
|
|
13
|
+
- **Tags** (`vX.Y.Z`) — the release artifact. A tag points at a commit
|
|
14
|
+
on `main`; `release.yml` builds and publishes from the tagged commit.
|
|
15
|
+
|
|
16
|
+
There is no separate "release branch". A release is a *tagged commit
|
|
17
|
+
on `main`*, not a branch. Multiple unreleased commits accumulate on
|
|
18
|
+
`main` between tags; tagging snapshots them into a release.
|
|
19
|
+
|
|
20
|
+
## Naming convention
|
|
21
|
+
|
|
22
|
+
Feature branches use a `<type>/<short-slug>` prefix matching the
|
|
23
|
+
[Conventional Commit](https://www.conventionalcommits.org/) types:
|
|
24
|
+
|
|
25
|
+
- `feat/<slug>` — new feature.
|
|
26
|
+
- `fix/<slug>` — bug fix.
|
|
27
|
+
- `docs/<slug>` — documentation only.
|
|
28
|
+
- `refactor/<slug>` — internal restructure, no behaviour change.
|
|
29
|
+
- `chore/<slug>` — tooling, dependency bumps, release prep.
|
|
30
|
+
- `ci/<slug>` — workflow / CI changes.
|
|
31
|
+
- `test/<slug>` — test-only changes.
|
|
32
|
+
|
|
33
|
+
Agent-generated branches may use the existing `claude/issue-<n>-<date>`
|
|
34
|
+
pattern; the prefix isn't load-bearing as long as the branch is
|
|
35
|
+
short-lived.
|
|
36
|
+
|
|
37
|
+
## Lifecycle of a change
|
|
38
|
+
|
|
39
|
+
1. Branch from latest `main`:
|
|
40
|
+
```sh
|
|
41
|
+
git checkout main && git pull
|
|
42
|
+
git checkout -b fix/<slug>
|
|
43
|
+
```
|
|
44
|
+
2. Commit using Conventional Commits (`fix(scope): subject`).
|
|
45
|
+
3. Push and open a PR against `main`. CI runs.
|
|
46
|
+
4. Once green and (if applicable) reviewed, **squash-merge** to `main`.
|
|
47
|
+
Squashing keeps `main` one-commit-per-PR — easy to scan, easy to
|
|
48
|
+
revert.
|
|
49
|
+
5. Delete the feature branch (`gh pr merge --delete-branch`, or the
|
|
50
|
+
"Delete branch" button on the PR).
|
|
51
|
+
|
|
52
|
+
The squashed commit subject follows `<type>(scope): subject (#N)` —
|
|
53
|
+
matching the existing convention seen on PRs #8, #10, #26.
|
|
54
|
+
|
|
55
|
+
## Branch protection on `main`
|
|
56
|
+
|
|
57
|
+
`main` is protected. The exact rules are configured via the GitHub
|
|
58
|
+
branch-protection API; the current policy is:
|
|
59
|
+
|
|
60
|
+
- **No direct pushes.** Every change goes through a pull request.
|
|
61
|
+
- **Required status checks** must pass before merge:
|
|
62
|
+
- `lint`
|
|
63
|
+
- `test (ubuntu-latest, 3.12)`
|
|
64
|
+
- **Force pushes blocked.**
|
|
65
|
+
- **Branch deletion blocked.**
|
|
66
|
+
- **Administrators are not exempt.** Even repo owners go through PRs.
|
|
67
|
+
|
|
68
|
+
To inspect or change the rule:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
gh api repos/:owner/:repo/branches/main/protection
|
|
72
|
+
gh api repos/:owner/:repo/branches/main/protection -X PUT --input rules.json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If a status-check job is renamed in CI, update `contexts` in the
|
|
76
|
+
protection rule the same day — otherwise PRs will hang waiting for a
|
|
77
|
+
check that no longer runs.
|
|
78
|
+
|
|
79
|
+
## Releases vs. branches
|
|
80
|
+
|
|
81
|
+
Releases are a *separate* concern from branching:
|
|
82
|
+
|
|
83
|
+
- Release prep (CHANGELOG roll, version bumps in `pyproject.toml` and
|
|
84
|
+
`nsc/_version.py`) happens on a `chore/release-X.Y.Z` branch.
|
|
85
|
+
- Open a PR for the release prep, merge to `main`, **then** tag the
|
|
86
|
+
resulting merge commit:
|
|
87
|
+
```sh
|
|
88
|
+
git checkout main && git pull
|
|
89
|
+
git tag -a vX.Y.Z -m "vX.Y.Z"
|
|
90
|
+
git push origin vX.Y.Z
|
|
91
|
+
```
|
|
92
|
+
- The tag triggers `release.yml`, which builds the wheel + sdist and
|
|
93
|
+
publishes to PyPI.
|
|
94
|
+
- See [`release-process.md`](release-process.md) for the full checklist.
|
|
95
|
+
|
|
96
|
+
We do **not** maintain release branches (`release/v1.x`) at this time.
|
|
97
|
+
Milestones (e.g. `v1.0.2`, `v1.1.0`) track *what's planned* for which
|
|
98
|
+
release without forking the codebase.
|
|
99
|
+
|
|
100
|
+
## Why no long-lived release branches?
|
|
101
|
+
|
|
102
|
+
For a single-trunk project, release branches add merge overhead
|
|
103
|
+
(every fix needs two homes) without benefit. A milestone tells us
|
|
104
|
+
"this issue is scoped to v1.0.2"; the actual code change lives on
|
|
105
|
+
`main` and ships when we tag.
|
|
106
|
+
|
|
107
|
+
The day we need to support v1.x and v2.x simultaneously, we'll cut
|
|
108
|
+
`release/v1.x` from the last v1.* tag and start cherry-picking
|
|
109
|
+
qualifying fixes into it. Until then, `main` is the only place code
|
|
110
|
+
lives.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Release process
|
|
2
|
+
|
|
3
|
+
`nsc` follows [Semantic Versioning](https://semver.org/) from v1.0.0 onward.
|
|
4
|
+
Releases are tag-driven: `release.yml` fires on `v*.*.*` tag pushes and
|
|
5
|
+
publishes to PyPI via trusted publishing (no API tokens).
|
|
6
|
+
|
|
7
|
+
A release is a **tagged commit on `main`**, not a separate branch — see
|
|
8
|
+
[Branching and merging](branching.md) for the trunk-based workflow this
|
|
9
|
+
process assumes. Release prep (CHANGELOG roll + version bumps) happens
|
|
10
|
+
on a `chore/release-X.Y.Z` feature branch, gets PR-merged into `main`,
|
|
11
|
+
and only then is `vX.Y.Z` tagged on the resulting merge commit.
|
|
12
|
+
|
|
13
|
+
## Normal release checklist
|
|
14
|
+
|
|
15
|
+
1. Full unit suite green: `just test`.
|
|
16
|
+
2. Lint clean: `just lint`.
|
|
17
|
+
3. Bench median <300 ms: `just bench`.
|
|
18
|
+
4. E2E green (requires Docker): `just e2e`.
|
|
19
|
+
5. Roll `CHANGELOG.md`: move `[Unreleased]` to `[vX.Y.Z] — YYYY-MM-DD`.
|
|
20
|
+
6. Bump `pyproject.toml` `[project].version` and `nsc/_version.py` `__version__`
|
|
21
|
+
to match the tag.
|
|
22
|
+
7. Verify all issues attached to the matching
|
|
23
|
+
[GitHub milestone](https://github.com/thomaschristory/netbox-super-cli/milestones)
|
|
24
|
+
are resolved or deferred (don't close the milestone yet — that happens
|
|
25
|
+
after publish, see below).
|
|
26
|
+
8. Commit: `git commit -m "chore(release): X.Y.Z"`.
|
|
27
|
+
9. Annotate and push: `git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin main vX.Y.Z`.
|
|
28
|
+
10. Watch `release.yml` publish the wheel and create the GitHub Release.
|
|
29
|
+
11. Once PyPI shows the new version and the GitHub Release is live, close
|
|
30
|
+
the milestone and open the next one — see the next section for the
|
|
31
|
+
exact `gh api` invocations.
|
|
32
|
+
|
|
33
|
+
## GitHub milestones convention
|
|
34
|
+
|
|
35
|
+
Each planned release has a milestone named after its version (e.g., `v1.1.0`).
|
|
36
|
+
Issues and PRs are triaged to a milestone when they are scoped to a specific
|
|
37
|
+
release. Milestones don't gate CI — they're a planning aid that surfaces
|
|
38
|
+
what's queued for which version.
|
|
39
|
+
|
|
40
|
+
- Milestone name: `vX.Y.Z` (matches the git tag exactly).
|
|
41
|
+
- An open milestone with no due date means "planned but not scheduled".
|
|
42
|
+
- The `main` branch is always releasable; milestones don't block merges.
|
|
43
|
+
|
|
44
|
+
**Once a release is published**, close its milestone and open the next one.
|
|
45
|
+
This is step 11 of the release checklist; the exact commands are:
|
|
46
|
+
|
|
47
|
+
1. Bump or close any open issues left in the just-shipped milestone —
|
|
48
|
+
either move them to the next milestone or drop them to the backlog.
|
|
49
|
+
2. Close the milestone:
|
|
50
|
+
```sh
|
|
51
|
+
gh api repos/:owner/:repo/milestones/<N> -X PATCH -f state=closed
|
|
52
|
+
```
|
|
53
|
+
3. Open the next one:
|
|
54
|
+
```sh
|
|
55
|
+
gh api repos/:owner/:repo/milestones -f title='v<next>' -f state=open
|
|
56
|
+
```
|
|
57
|
+
For a patch release, increment the patch number; for a minor/major,
|
|
58
|
+
pick whatever's next based on what's queued.
|
|
59
|
+
4. Leave the new milestone otherwise empty — issues get attached as they're
|
|
60
|
+
triaged.
|
|
61
|
+
|
|
62
|
+
This keeps a milestone always available to file new issues against without
|
|
63
|
+
having to think about it mid-bug-report.
|
|
64
|
+
|
|
65
|
+
## Patch releases (bug fixes)
|
|
66
|
+
|
|
67
|
+
Patch bumps (`Z` increment) follow the same checklist. Because they contain
|
|
68
|
+
only bug fixes, the E2E run can be skipped if the fix is confined to a code
|
|
69
|
+
path not exercised by the E2E suite — but running it is still recommended.
|
|
70
|
+
|
|
71
|
+
## Minor / major releases
|
|
72
|
+
|
|
73
|
+
Minor (`Y`) or major (`X`) bumps follow the full checklist. Before tagging,
|
|
74
|
+
open a tracking issue or milestone for any follow-on items deferred from the
|
|
75
|
+
release.
|
|
76
|
+
|
|
77
|
+
## Hot-fixing a tagged release
|
|
78
|
+
|
|
79
|
+
Never amend a tagged commit. Increment the patch component (e.g.,
|
|
80
|
+
`v1.0.1` → `v1.0.2`), cut a fresh patch release with the fix, and document
|
|
81
|
+
it in `CHANGELOG.md` under a new `[vX.Y.Z]` entry for the new version.
|
|
82
|
+
|
|
83
|
+
## Versioning policy
|
|
84
|
+
|
|
85
|
+
From v1.0.0 onward:
|
|
86
|
+
|
|
87
|
+
- `PATCH` — backwards-compatible bug fixes.
|
|
88
|
+
- `MINOR` — new backwards-compatible features or CLI commands.
|
|
89
|
+
- `MAJOR` — breaking changes to the error envelope, exit codes, or config schema.
|
|
90
|
+
|
|
91
|
+
The version in `pyproject.toml`, `nsc/_version.py`, and the git tag must
|
|
92
|
+
always agree. `release.yml` validates the tag against `nsc/_version.py`
|
|
93
|
+
before publishing; `pyproject.toml` is not currently checked, so keep it
|
|
94
|
+
in sync manually as part of the release commit.
|
|
@@ -4,8 +4,6 @@ Dynamic NetBox CLI driven by the live OpenAPI schema. The same `nsc` binary work
|
|
|
4
4
|
against any NetBox version (4.4+) and exposes plugin-provided endpoints automatically
|
|
5
5
|
because the schema — not hand-written code — defines the surface.
|
|
6
6
|
|
|
7
|
-
> **Status:** Phase 5b. v1.0.0 publish lands in 5d. Until then, install from source.
|
|
8
|
-
|
|
9
7
|
## Why nsc
|
|
10
8
|
|
|
11
9
|
- **Plugins just work.** New endpoints from any installed plugin appear as commands automatically.
|
|
@@ -11,7 +11,7 @@ All fields below describe `~/.nsc/config.yaml`.
|
|
|
11
11
|
|---|---|---|
|
|
12
12
|
| `default_profile` | `str | None` | `None` |
|
|
13
13
|
| `profiles` | `dict[str, Profile]` | `{}` |
|
|
14
|
-
| `defaults` | `<class 'Defaults'>` | `Defaults(output=<OutputFormat.TABLE: 'table'>, page_size=50, timeout=30.0, schema_refresh=<SchemaRefresh.
|
|
14
|
+
| `defaults` | `<class 'Defaults'>` | `Defaults(output=<OutputFormat.TABLE: 'table'>, page_size=50, timeout=30.0, schema_refresh=<SchemaRefresh.DAILY: 'daily'>)` |
|
|
15
15
|
| `columns` | `dict[str, dict[str, list[str]]]` | `{}` |
|
|
16
16
|
|
|
17
17
|
## `Profile`
|
|
@@ -32,4 +32,4 @@ All fields below describe `~/.nsc/config.yaml`.
|
|
|
32
32
|
| `output` | `<enum 'OutputFormat'>` | `<OutputFormat.TABLE: 'table'>` |
|
|
33
33
|
| `page_size` | `<class 'int'>` | `50` |
|
|
34
34
|
| `timeout` | `<class 'float'>` | `30.0` |
|
|
35
|
-
| `schema_refresh` | `<enum 'SchemaRefresh'>` | `<SchemaRefresh.
|
|
35
|
+
| `schema_refresh` | `<enum 'SchemaRefresh'>` | `<SchemaRefresh.DAILY: 'daily'>` |
|
|
@@ -75,5 +75,6 @@ nav:
|
|
|
75
75
|
- Caching: architecture/caching.md
|
|
76
76
|
- Contributing:
|
|
77
77
|
- Development: contributing/development.md
|
|
78
|
+
- Branching and merging: contributing/branching.md
|
|
78
79
|
- Release process: contributing/release-process.md
|
|
79
80
|
- Adding bundled schemas: contributing/adding-bundled-schemas.md
|