netbox-super-cli 1.6.2__tar.gz → 1.6.3__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.
Files changed (335) hide show
  1. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/CHANGELOG.md +19 -0
  2. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/PKG-INFO +1 -1
  3. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/_version.py +1 -1
  4. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cache/store.py +11 -3
  5. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/source.py +22 -0
  6. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/pyproject.toml +1 -1
  7. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cache/test_store.py +14 -0
  8. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_source.py +72 -1
  9. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/uv.lock +1 -1
  10. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  11. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  12. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/docs.yml +0 -0
  13. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  14. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/dependabot.yml +0 -0
  15. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/pull_request_template.md +0 -0
  16. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/bench.yml +0 -0
  17. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/claude.yml +0 -0
  18. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/docs.yml +0 -0
  19. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/e2e.yml +0 -0
  20. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/lint.yml +0 -0
  21. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/release.yml +0 -0
  22. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/skill-convention-review.yml +0 -0
  23. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/test.yml +0 -0
  24. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.gitignore +0 -0
  25. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.pre-commit-config.yaml +0 -0
  26. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.python-version +0 -0
  27. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/AGENTS.md +0 -0
  28. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/CLAUDE.md +0 -0
  29. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/LICENSE +0 -0
  30. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/README.md +0 -0
  31. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/caching.md +0 -0
  32. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/command-generation.md +0 -0
  33. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/http-client.md +0 -0
  34. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/overview.md +0 -0
  35. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/schema-loading.md +0 -0
  36. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/detail.svg +0 -0
  37. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/filter.svg +0 -0
  38. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/list.svg +0 -0
  39. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/picker.svg +0 -0
  40. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/search.svg +0 -0
  41. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/adding-bundled-schemas.md +0 -0
  42. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/branching.md +0 -0
  43. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/development.md +0 -0
  44. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/release-process.md +0 -0
  45. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/getting-started/concepts.md +0 -0
  46. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/getting-started/first-run.md +0 -0
  47. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/getting-started/install.md +0 -0
  48. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/audit-log.md +0 -0
  49. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/ci-and-automation.md +0 -0
  50. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/interactive-tui.md +0 -0
  51. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/managing-profiles.md +0 -0
  52. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/output-formats.md +0 -0
  53. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/using-with-ai-agents.md +0 -0
  54. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/working-with-plugins.md +0 -0
  55. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/writes-and-safety.md +0 -0
  56. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/index.md +0 -0
  57. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/reference/cli.md +0 -0
  58. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/reference/config.md +0 -0
  59. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/reference/exit-codes.md +0 -0
  60. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/reference/schemas.md +0 -0
  61. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/justfile +0 -0
  62. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/mkdocs.yml +0 -0
  63. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/__init__.py +0 -0
  64. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/__main__.py +0 -0
  65. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/aliases/__init__.py +0 -0
  66. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/aliases/resolver.py +0 -0
  67. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/auth/__init__.py +0 -0
  68. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/auth/verify.py +0 -0
  69. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/builder/__init__.py +0 -0
  70. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/builder/build.py +0 -0
  71. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cache/__init__.py +0 -0
  72. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/__init__.py +0 -0
  73. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/aliases_commands.py +0 -0
  74. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/app.py +0 -0
  75. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/cache_commands.py +0 -0
  76. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/commands_dump.py +0 -0
  77. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/config_commands.py +0 -0
  78. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/globals.py +0 -0
  79. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/handlers.py +0 -0
  80. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/init_commands.py +0 -0
  81. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/login_commands.py +0 -0
  82. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/profiles_commands.py +0 -0
  83. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/registration.py +0 -0
  84. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/runtime.py +0 -0
  85. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/skill_commands.py +0 -0
  86. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/tui_commands.py +0 -0
  87. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/__init__.py +0 -0
  88. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/apply.py +0 -0
  89. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/bulk.py +0 -0
  90. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/coercion.py +0 -0
  91. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/confirmation.py +0 -0
  92. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/input.py +0 -0
  93. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/preflight.py +0 -0
  94. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/completion/__init__.py +0 -0
  95. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/completion/cache_probe.py +0 -0
  96. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/completion/callbacks.py +0 -0
  97. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/completion/providers.py +0 -0
  98. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/__init__.py +0 -0
  99. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/loader.py +0 -0
  100. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/models.py +0 -0
  101. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/saved_searches.py +0 -0
  102. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/settings.py +0 -0
  103. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/writer.py +0 -0
  104. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/__init__.py +0 -0
  105. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/audit.py +0 -0
  106. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/client.py +0 -0
  107. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/errors.py +0 -0
  108. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/retry.py +0 -0
  109. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/model/__init__.py +0 -0
  110. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/model/command_model.py +0 -0
  111. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/__init__.py +0 -0
  112. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/_console.py +0 -0
  113. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/colors.py +0 -0
  114. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/csv_.py +0 -0
  115. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/errors.py +0 -0
  116. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/explain.py +0 -0
  117. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/flatten.py +0 -0
  118. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/headers.py +0 -0
  119. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/json_.py +0 -0
  120. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/jsonl.py +0 -0
  121. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/render.py +0 -0
  122. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/table.py +0 -0
  123. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/yaml_.py +0 -0
  124. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/__init__.py +0 -0
  125. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/custom_fields.py +0 -0
  126. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/objecttypes.py +0 -0
  127. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/params.py +0 -0
  128. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/store.py +0 -0
  129. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/tags.py +0 -0
  130. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/__init__.py +0 -0
  131. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/hashing.py +0 -0
  132. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/loader.py +0 -0
  133. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/models.py +0 -0
  134. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/__init__.py +0 -0
  135. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/bundled/__init__.py +0 -0
  136. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/bundled/manifest.yaml +0 -0
  137. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/bundled/netbox-4.5.10.json.gz +0 -0
  138. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/bundled/netbox-4.6.0.json.gz +0 -0
  139. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/skill/__init__.py +0 -0
  140. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/__init__.py +0 -0
  141. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/_bindings.py +0 -0
  142. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/app.py +0 -0
  143. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/bulk.py +0 -0
  144. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/catalog.py +0 -0
  145. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/columns.py +0 -0
  146. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/errors.py +0 -0
  147. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/filters.py +0 -0
  148. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/fk.py +0 -0
  149. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/forms.py +0 -0
  150. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/keymap.py +0 -0
  151. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/nav.py +0 -0
  152. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/relations.py +0 -0
  153. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/__init__.py +0 -0
  154. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/bulk_edit_form.py +0 -0
  155. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/columns.py +0 -0
  156. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/detail.py +0 -0
  157. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/edit_form.py +0 -0
  158. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/filter.py +0 -0
  159. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/global_search.py +0 -0
  160. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/list.py +0 -0
  161. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/picker.py +0 -0
  162. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/record_picker.py +0 -0
  163. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/saved_search_picker.py +0 -0
  164. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/search.py +0 -0
  165. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/selection.py +0 -0
  166. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/styles.tcss +0 -0
  167. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/view.py +0 -0
  168. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/__init__.py +0 -0
  169. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/_modal.py +0 -0
  170. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/bulk_diff.py +0 -0
  171. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/bulk_summary.py +0 -0
  172. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/confirm.py +0 -0
  173. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/diff.py +0 -0
  174. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/help.py +0 -0
  175. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/nav_tree.py +0 -0
  176. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/scripts/gen_docs.py +0 -0
  177. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/scripts/sync_agents_md.py +0 -0
  178. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/skills/netbox-super-cli/SKILL.md +0 -0
  179. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/__init__.py +0 -0
  180. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/aliases/__init__.py +0 -0
  181. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/aliases/test_resolver.py +0 -0
  182. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/audit/__init__.py +0 -0
  183. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/audit/test_redaction_full.py +0 -0
  184. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/auth/__init__.py +0 -0
  185. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/auth/test_verify.py +0 -0
  186. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/benchmarks/__init__.py +0 -0
  187. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/benchmarks/test_startup.py +0 -0
  188. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/__init__.py +0 -0
  189. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_build.py +0 -0
  190. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_default_columns.py +0 -0
  191. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_fk_metadata.py +0 -0
  192. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_redaction_marking.py +0 -0
  193. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_request_body_shape.py +0 -0
  194. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cache/__init__.py +0 -0
  195. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cache/test_prune.py +0 -0
  196. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/__init__.py +0 -0
  197. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_aliases_commands.py +0 -0
  198. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_app_smoke.py +0 -0
  199. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_cache_commands.py +0 -0
  200. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_commands_dump.py +0 -0
  201. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_completion_protocol_smoke.py +0 -0
  202. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_completion_smoke.py +0 -0
  203. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_config_commands.py +0 -0
  204. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_dynamic_completion.py +0 -0
  205. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_explain.py +0 -0
  206. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_globals_color.py +0 -0
  207. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_handlers.py +0 -0
  208. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_init_commands.py +0 -0
  209. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_lazy_textual_import.py +0 -0
  210. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_login_commands.py +0 -0
  211. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_meta_subcommands_under_broken_config.py +0 -0
  212. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_ndjson_input.py +0 -0
  213. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_profiles_commands.py +0 -0
  214. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_registration.py +0 -0
  215. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_respx_integration.py +0 -0
  216. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_runtime.py +0 -0
  217. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_runtime_color.py +0 -0
  218. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_skill_commands.py +0 -0
  219. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_stdin_sniffer.py +0 -0
  220. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_tui_command.py +0 -0
  221. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_writes_respx.py +0 -0
  222. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/__init__.py +0 -0
  223. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_apply.py +0 -0
  224. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_bulk.py +0 -0
  225. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_confirmation.py +0 -0
  226. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_handler_helpers.py +0 -0
  227. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_handlers_audit.py +0 -0
  228. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_input.py +0 -0
  229. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_preflight.py +0 -0
  230. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/__init__.py +0 -0
  231. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_loader.py +0 -0
  232. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_models.py +0 -0
  233. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_saved_search_fallback.py +0 -0
  234. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_saved_searches.py +0 -0
  235. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_settings_private_dir.py +0 -0
  236. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_writer_atomicity.py +0 -0
  237. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_writer_dotted_paths.py +0 -0
  238. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_writer_roundtrip.py +0 -0
  239. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/conftest.py +0 -0
  240. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/README.md +0 -0
  241. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/__init__.py +0 -0
  242. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/conftest.py +0 -0
  243. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/docker-compose.yml +0 -0
  244. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_audit_redaction.py +0 -0
  245. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_auth_error_envelope.py +0 -0
  246. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_bulk_create_with_loop_fallback.py +0 -0
  247. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_full_cycle.py +0 -0
  248. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_login.py +0 -0
  249. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_ndjson.py +0 -0
  250. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_preflight_blocks_apply.py +0 -0
  251. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_validation_error_envelope_from_netbox.py +0 -0
  252. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/wait_for_netbox.sh +0 -0
  253. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/profiles/single_profile.yaml +0 -0
  254. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/auth_401.json +0 -0
  255. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/circuits_providers_list.json +0 -0
  256. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/dcim_devices_get.json +0 -0
  257. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/dcim_devices_list_p1.json +0 -0
  258. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/dcim_devices_list_p2.json +0 -0
  259. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/__init__.py +0 -0
  260. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_audit.py +0 -0
  261. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_audit_permissions.py +0 -0
  262. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_audit_redaction.py +0 -0
  263. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_client.py +0 -0
  264. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_client_redaction_threading.py +0 -0
  265. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_errors.py +0 -0
  266. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_retry.py +0 -0
  267. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/model/__init__.py +0 -0
  268. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/model/test_command_model.py +0 -0
  269. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/model/test_request_body_shape.py +0 -0
  270. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/__init__.py +0 -0
  271. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_colors.py +0 -0
  272. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_console.py +0 -0
  273. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_csv.py +0 -0
  274. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_errors.py +0 -0
  275. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_errors_aliases.py +0 -0
  276. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_errors_color.py +0 -0
  277. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_explain.py +0 -0
  278. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_explain_color.py +0 -0
  279. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_flatten.py +0 -0
  280. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_input_error_envelope.py +0 -0
  281. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_json.py +0 -0
  282. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_jsonl.py +0 -0
  283. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_render_dispatch.py +0 -0
  284. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_table.py +0 -0
  285. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_table_color.py +0 -0
  286. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_yaml.py +0 -0
  287. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/__init__.py +0 -0
  288. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_custom_fields.py +0 -0
  289. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_objecttypes.py +0 -0
  290. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_params.py +0 -0
  291. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_store.py +0 -0
  292. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_tags.py +0 -0
  293. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/__init__.py +0 -0
  294. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_hashing.py +0 -0
  295. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_loader.py +0 -0
  296. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_models.py +0 -0
  297. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_source_ttl.py +0 -0
  298. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/scripts/__init__.py +0 -0
  299. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/scripts/test_gen_docs.py +0 -0
  300. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/scripts/test_sync_agents_md.py +0 -0
  301. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/skill/__init__.py +0 -0
  302. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/skill/test_bundle.py +0 -0
  303. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/test_packaging.py +0 -0
  304. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/__init__.py +0 -0
  305. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_app.py +0 -0
  306. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bindings.py +0 -0
  307. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_apply.py +0 -0
  308. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_diff.py +0 -0
  309. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_diff_modal.py +0 -0
  310. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_edit_form.py +0 -0
  311. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_import_isolation.py +0 -0
  312. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_summary.py +0 -0
  313. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_catalog.py +0 -0
  314. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_column_chooser.py +0 -0
  315. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_columns.py +0 -0
  316. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_detail_screen.py +0 -0
  317. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_diff.py +0 -0
  318. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_diff_modal.py +0 -0
  319. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_edit_form.py +0 -0
  320. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_errors.py +0 -0
  321. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_filter_screen.py +0 -0
  322. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_filters.py +0 -0
  323. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_fk_resolve.py +0 -0
  324. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_forms.py +0 -0
  325. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_global_search.py +0 -0
  326. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_help_overlay.py +0 -0
  327. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_keymap.py +0 -0
  328. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_list_screen.py +0 -0
  329. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_picker.py +0 -0
  330. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_record_picker.py +0 -0
  331. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_relations.py +0 -0
  332. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_saved_filter_integration.py +0 -0
  333. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_search.py +0 -0
  334. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_selection.py +0 -0
  335. {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_view.py +0 -0
@@ -2,6 +2,25 @@
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
+ ## v1.6.3 — 2026-07-03
6
+
7
+ Patch release. Stops an offline instance from rebuilding the command model from
8
+ the bundled schema on every invocation after a format-version bump.
9
+
10
+ ### Fixed
11
+
12
+ - **Offline invocations rebuilt from the bundled schema every time when the only
13
+ cached model was format-stale** ([#140]). After the v1.6.2 format-version bump
14
+ (#139), a cache entry built by an older `nsc` is rejected so it rebuilds with
15
+ the new metadata. Online this self-heals on the first call, but while NetBox
16
+ was unreachable nothing re-saved: each invocation re-parsed the bundled schema
17
+ (~180ms) and tab-completion went briefly blank for the profile. The offline
18
+ bundled fallback is now persisted under the profile — without a fetch
19
+ timestamp, so the TTL fast-path still refetches once NetBox is reachable — so
20
+ the next invocation hits the cache and completion recovers immediately.
21
+
22
+ [#140]: https://github.com/thomaschristory/netbox-super-cli/issues/140
23
+
5
24
  ## v1.6.2 — 2026-06-30
6
25
 
7
26
  Patch release. Fixes TUI foreign-key pickers resolving to the wrong resource.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netbox-super-cli
3
- Version: 1.6.2
3
+ Version: 1.6.3
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: Documentation, https://thomaschristory.github.io/netbox-super-cli/
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for the package version."""
2
2
 
3
- __version__ = "1.6.2"
3
+ __version__ = "1.6.3"
@@ -82,13 +82,21 @@ class CacheStore:
82
82
  return None
83
83
  return model
84
84
 
85
- def save(self, profile: str, model: CommandModel) -> Path:
85
+ def save(self, profile: str, model: CommandModel, *, record_fetch: bool = True) -> Path:
86
+ """Persist `model` under the profile. `record_fetch=True` also stamps
87
+ the `fetched_at` sidecar (a live fetch just produced this model, so
88
+ the TTL fast-path may trust it). Pass `record_fetch=False` for models
89
+ that were *not* fetched from this NetBox — e.g. the offline bundled
90
+ fallback (issue #140): we want the persisted copy so future offline
91
+ invocations skip the rebuild, but the TTL fast-path must keep
92
+ distrusting it so a real fetch is attempted once NetBox is reachable."""
86
93
  self._validate_profile(profile)
87
94
  target = self._path_for(profile, model.schema_hash)
88
95
  target.parent.mkdir(parents=True, exist_ok=True)
89
96
  _atomic_write(target, model.model_dump_json(indent=2))
90
- meta = self._meta_path_for(profile, model.schema_hash)
91
- _atomic_write(meta, json.dumps({"fetched_at": time.time()}))
97
+ if record_fetch:
98
+ meta = self._meta_path_for(profile, model.schema_hash)
99
+ _atomic_write(meta, json.dumps({"fetched_at": time.time()}))
92
100
  return target
93
101
 
94
102
  def touch_fetched_at(self, profile: str, schema_hash: str) -> None:
@@ -15,6 +15,7 @@ access logs as a `GET /api/schema/` between every PATCH.
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
+ import contextlib
18
19
  import sys
19
20
  import time
20
21
  from pathlib import Path
@@ -97,6 +98,7 @@ def resolve_command_model(
97
98
  return cached
98
99
  bundled = _load_bundled_command_model()
99
100
  if bundled is not None:
101
+ _persist_offline_fallback(paths, profile.name, bundled)
100
102
  print(
101
103
  f"Could not reach NetBox ({exc}) and no cache; using bundled schema.",
102
104
  file=sys.stderr,
@@ -197,6 +199,26 @@ def _find_fresh_cached(
197
199
  return None
198
200
 
199
201
 
202
+ def _persist_offline_fallback(paths: Paths, profile_name: str, model: CommandModel) -> None:
203
+ """Cache a bundled-schema model under the profile so subsequent offline
204
+ invocations hit `_find_any_cached` (a cheap JSON load) instead of
205
+ re-building from the bundled schema on every call (issue #140). This also
206
+ ends the completion blackout: `cache_probe` can now surface a
207
+ format-current entry.
208
+
209
+ No fetch timestamp is recorded (`record_fetch=False`): the model did not
210
+ come from this NetBox, so the TTL fast-path must keep distrusting it and
211
+ attempt a real fetch on the next invocation — self-healing once NetBox is
212
+ reachable again. A write failure here is non-fatal; we still return the
213
+ in-memory model to the caller."""
214
+ store = CacheStore(root=paths.cache_dir)
215
+ # OSError: disk/permission failure. ValueError: CacheStore rejects a
216
+ # profile name outside `_PROFILE_RE` (names aren't validated at config
217
+ # time). Both are non-fatal here — the in-memory model is still returned.
218
+ with contextlib.suppress(OSError, ValueError):
219
+ store.save(profile_name, model, record_fetch=False)
220
+
221
+
200
222
  def _load_bundled_command_model() -> CommandModel | None:
201
223
  pkg_dir = Path(_bundled_pkg.__file__).resolve().parent
202
224
  manifest_path = pkg_dir / "manifest.yaml"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "netbox-super-cli"
3
- version = "1.6.2"
3
+ version = "1.6.3"
4
4
  description = "Dynamic NetBox CLI generated from the live OpenAPI schema."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -148,6 +148,20 @@ def test_save_writes_fetched_at_sidecar(tmp_path: Path) -> None:
148
148
  assert before <= fetched_at <= after
149
149
 
150
150
 
151
+ def test_save_without_record_fetch_writes_no_sidecar(tmp_path: Path) -> None:
152
+ """`save(record_fetch=False)` persists the model but leaves no fetch
153
+ timestamp, so the TTL fast-path keeps distrusting the entry. Used by the
154
+ offline bundled-fallback path (issue #140): we cache the rebuilt model to
155
+ skip per-invocation rebuilds, but a real fetch must still be attempted
156
+ once NetBox is reachable again."""
157
+ store = CacheStore(root=tmp_path)
158
+ store.save("prod", _model("a" * 64), record_fetch=False)
159
+
160
+ assert store.load("prod", "a" * 64) is not None
161
+ assert not (tmp_path / "prod" / ("a" * 64 + ".meta.json")).exists()
162
+ assert store.load_fetched_at("prod", "a" * 64) is None
163
+
164
+
151
165
  def test_load_fetched_at_returns_none_when_missing(tmp_path: Path) -> None:
152
166
  """No sidecar means we can't prove freshness — distrust the entry."""
153
167
  store = CacheStore(root=tmp_path)
@@ -10,9 +10,11 @@ import httpx
10
10
  import pytest
11
11
  import respx
12
12
 
13
+ from nsc.cache.store import CacheStore
13
14
  from nsc.cli.runtime import ResolvedProfile
15
+ from nsc.completion.cache_probe import load_cached_model_for_profile
14
16
  from nsc.config.settings import Paths
15
- from nsc.model.command_model import CommandModel
17
+ from nsc.model.command_model import MODEL_FORMAT_VERSION, CommandModel
16
18
  from nsc.schema.source import (
17
19
  SchemaSourceError,
18
20
  resolve_command_model,
@@ -143,6 +145,75 @@ def test_offline_no_cache_falls_back_to_bundled(
143
145
  assert "bundled" in err.lower()
144
146
 
145
147
 
148
+ def _stale_the_only_cache_entry(paths: Paths, profile_name: str) -> str:
149
+ """Downgrade the single cached entry to a pre-versioning format so
150
+ `CacheStore.load` rejects it purely on `format_version`. Returns its hash."""
151
+ profile_dir = paths.cache_dir / profile_name
152
+ cache_files = [p for p in profile_dir.glob("*.json") if not p.name.endswith(".meta.json")]
153
+ assert len(cache_files) == 1
154
+ stale_file = cache_files[0]
155
+ data = json.loads(stale_file.read_text())
156
+ data["format_version"] = 0
157
+ stale_file.write_text(json.dumps(data))
158
+ return stale_file.stem
159
+
160
+
161
+ @respx.mock
162
+ def test_offline_format_stale_cache_persists_bundled_fallback(
163
+ tmp_path: Path, capsys: pytest.CaptureFixture[str]
164
+ ) -> None:
165
+ """Issue #140: when the only cached entry is format-stale (hash-fresh) and
166
+ NetBox is unreachable, the bundled fallback is persisted under the profile
167
+ so the next invocation skips the per-call rebuild — without a fetch
168
+ timestamp, so the TTL fast-path still refetches once NetBox returns."""
169
+ route = respx.get("https://nb.example/api/schema/?format=json").mock(
170
+ return_value=httpx.Response(200, json=_minimal_schema_doc())
171
+ )
172
+ paths = _paths(tmp_path)
173
+ profile = _profile()
174
+ resolve_command_model(paths=paths, profile=profile, schema_override=None)
175
+ stale_hash = _stale_the_only_cache_entry(paths, "prod")
176
+ capsys.readouterr() # drain
177
+
178
+ route.mock(side_effect=httpx.ConnectError("offline"))
179
+ first = resolve_command_model(paths=paths, profile=profile, schema_override=None)
180
+ assert first.format_version == MODEL_FORMAT_VERSION
181
+ assert "bundled" in capsys.readouterr().err.lower()
182
+
183
+ # The bundled model is now persisted under the profile (a distinct hash),
184
+ # carries no fetch timestamp, and is visible to completion — no blackout.
185
+ store = CacheStore(root=paths.cache_dir)
186
+ assert first.schema_hash != stale_hash
187
+ assert store.load("prod", first.schema_hash) is not None
188
+ assert store.load_fetched_at("prod", first.schema_hash) is None
189
+ probed = load_cached_model_for_profile(paths, "prod")
190
+ assert probed is not None
191
+ assert probed.format_version == MODEL_FORMAT_VERSION
192
+
193
+ # Second offline invocation hits `_find_any_cached` instead of rebuilding.
194
+ second = resolve_command_model(paths=paths, profile=profile, schema_override=None)
195
+ assert second.schema_hash == first.schema_hash
196
+ assert "cached" in capsys.readouterr().err.lower()
197
+
198
+
199
+ @respx.mock
200
+ def test_offline_bundled_fallback_survives_unpersistable_profile_name(
201
+ tmp_path: Path, capsys: pytest.CaptureFixture[str]
202
+ ) -> None:
203
+ """A profile name outside `_PROFILE_RE` (names aren't validated at config
204
+ time) makes `CacheStore.save` raise `ValueError`; the offline bundled
205
+ fallback must swallow it and still return the in-memory model."""
206
+ respx.get("https://nb.example/api/schema/?format=json").mock(
207
+ side_effect=httpx.ConnectError("offline")
208
+ )
209
+ paths = _paths(tmp_path)
210
+ model = resolve_command_model(
211
+ paths=paths, profile=_profile(name="has space"), schema_override=None
212
+ )
213
+ assert isinstance(model, CommandModel)
214
+ assert "bundled" in capsys.readouterr().err.lower()
215
+
216
+
146
217
  @respx.mock
147
218
  def test_offline_no_cache_no_bundled_raises(
148
219
  tmp_path: Path, monkeypatch: pytest.MonkeyPatch
@@ -675,7 +675,7 @@ wheels = [
675
675
 
676
676
  [[package]]
677
677
  name = "netbox-super-cli"
678
- version = "1.6.2"
678
+ version = "1.6.3"
679
679
  source = { editable = "." }
680
680
  dependencies = [
681
681
  { name = "click" },