netbox-super-cli 1.0.0__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 (209) hide show
  1. netbox_super_cli-1.0.0/.github/workflows/agents-md-sync.yml +35 -0
  2. netbox_super_cli-1.0.0/.github/workflows/bench.yml +23 -0
  3. netbox_super_cli-1.0.0/.github/workflows/docs.yml +55 -0
  4. netbox_super_cli-1.0.0/.github/workflows/e2e.yml +42 -0
  5. netbox_super_cli-1.0.0/.github/workflows/lint.yml +56 -0
  6. netbox_super_cli-1.0.0/.github/workflows/release.yml +163 -0
  7. netbox_super_cli-1.0.0/.github/workflows/test.yml +27 -0
  8. netbox_super_cli-1.0.0/.gitignore +37 -0
  9. netbox_super_cli-1.0.0/.pre-commit-config.yaml +35 -0
  10. netbox_super_cli-1.0.0/.python-version +1 -0
  11. netbox_super_cli-1.0.0/AGENTS.md +45 -0
  12. netbox_super_cli-1.0.0/CHANGELOG.md +268 -0
  13. netbox_super_cli-1.0.0/CLAUDE.md +38 -0
  14. netbox_super_cli-1.0.0/LICENSE +201 -0
  15. netbox_super_cli-1.0.0/PKG-INFO +182 -0
  16. netbox_super_cli-1.0.0/README.md +155 -0
  17. netbox_super_cli-1.0.0/docs/architecture/caching.md +46 -0
  18. netbox_super_cli-1.0.0/docs/architecture/command-generation.md +56 -0
  19. netbox_super_cli-1.0.0/docs/architecture/http-client.md +44 -0
  20. netbox_super_cli-1.0.0/docs/architecture/overview.md +44 -0
  21. netbox_super_cli-1.0.0/docs/architecture/schema-loading.md +46 -0
  22. netbox_super_cli-1.0.0/docs/contributing/adding-bundled-schemas.md +61 -0
  23. netbox_super_cli-1.0.0/docs/contributing/development.md +49 -0
  24. netbox_super_cli-1.0.0/docs/contributing/release-process.md +42 -0
  25. netbox_super_cli-1.0.0/docs/getting-started/concepts.md +92 -0
  26. netbox_super_cli-1.0.0/docs/getting-started/first-run.md +72 -0
  27. netbox_super_cli-1.0.0/docs/getting-started/install.md +62 -0
  28. netbox_super_cli-1.0.0/docs/guides/ci-and-automation.md +98 -0
  29. netbox_super_cli-1.0.0/docs/guides/managing-profiles.md +73 -0
  30. netbox_super_cli-1.0.0/docs/guides/output-formats.md +61 -0
  31. netbox_super_cli-1.0.0/docs/guides/using-with-ai-agents.md +71 -0
  32. netbox_super_cli-1.0.0/docs/guides/working-with-plugins.md +46 -0
  33. netbox_super_cli-1.0.0/docs/guides/writes-and-safety.md +95 -0
  34. netbox_super_cli-1.0.0/docs/index.md +36 -0
  35. netbox_super_cli-1.0.0/docs/reference/cli.md +1648 -0
  36. netbox_super_cli-1.0.0/docs/reference/config.md +35 -0
  37. netbox_super_cli-1.0.0/docs/reference/exit-codes.md +24 -0
  38. netbox_super_cli-1.0.0/docs/reference/schemas.md +10 -0
  39. netbox_super_cli-1.0.0/justfile +62 -0
  40. netbox_super_cli-1.0.0/mkdocs.yml +79 -0
  41. netbox_super_cli-1.0.0/nsc/__init__.py +5 -0
  42. netbox_super_cli-1.0.0/nsc/__main__.py +6 -0
  43. netbox_super_cli-1.0.0/nsc/_version.py +3 -0
  44. netbox_super_cli-1.0.0/nsc/aliases/__init__.py +24 -0
  45. netbox_super_cli-1.0.0/nsc/aliases/resolver.py +112 -0
  46. netbox_super_cli-1.0.0/nsc/auth/__init__.py +5 -0
  47. netbox_super_cli-1.0.0/nsc/auth/verify.py +143 -0
  48. netbox_super_cli-1.0.0/nsc/builder/__init__.py +5 -0
  49. netbox_super_cli-1.0.0/nsc/builder/build.py +514 -0
  50. netbox_super_cli-1.0.0/nsc/cache/__init__.py +5 -0
  51. netbox_super_cli-1.0.0/nsc/cache/store.py +295 -0
  52. netbox_super_cli-1.0.0/nsc/cli/__init__.py +1 -0
  53. netbox_super_cli-1.0.0/nsc/cli/aliases_commands.py +264 -0
  54. netbox_super_cli-1.0.0/nsc/cli/app.py +291 -0
  55. netbox_super_cli-1.0.0/nsc/cli/cache_commands.py +159 -0
  56. netbox_super_cli-1.0.0/nsc/cli/commands_dump.py +57 -0
  57. netbox_super_cli-1.0.0/nsc/cli/config_commands.py +156 -0
  58. netbox_super_cli-1.0.0/nsc/cli/globals.py +65 -0
  59. netbox_super_cli-1.0.0/nsc/cli/handlers.py +660 -0
  60. netbox_super_cli-1.0.0/nsc/cli/init_commands.py +95 -0
  61. netbox_super_cli-1.0.0/nsc/cli/login_commands.py +265 -0
  62. netbox_super_cli-1.0.0/nsc/cli/profiles_commands.py +256 -0
  63. netbox_super_cli-1.0.0/nsc/cli/registration.py +465 -0
  64. netbox_super_cli-1.0.0/nsc/cli/runtime.py +290 -0
  65. netbox_super_cli-1.0.0/nsc/cli/skill_commands.py +186 -0
  66. netbox_super_cli-1.0.0/nsc/cli/writes/__init__.py +10 -0
  67. netbox_super_cli-1.0.0/nsc/cli/writes/apply.py +177 -0
  68. netbox_super_cli-1.0.0/nsc/cli/writes/bulk.py +231 -0
  69. netbox_super_cli-1.0.0/nsc/cli/writes/coercion.py +9 -0
  70. netbox_super_cli-1.0.0/nsc/cli/writes/confirmation.py +96 -0
  71. netbox_super_cli-1.0.0/nsc/cli/writes/input.py +358 -0
  72. netbox_super_cli-1.0.0/nsc/cli/writes/preflight.py +182 -0
  73. netbox_super_cli-1.0.0/nsc/config/__init__.py +23 -0
  74. netbox_super_cli-1.0.0/nsc/config/loader.py +69 -0
  75. netbox_super_cli-1.0.0/nsc/config/models.py +54 -0
  76. netbox_super_cli-1.0.0/nsc/config/settings.py +36 -0
  77. netbox_super_cli-1.0.0/nsc/config/writer.py +207 -0
  78. netbox_super_cli-1.0.0/nsc/http/__init__.py +6 -0
  79. netbox_super_cli-1.0.0/nsc/http/audit.py +183 -0
  80. netbox_super_cli-1.0.0/nsc/http/client.py +365 -0
  81. netbox_super_cli-1.0.0/nsc/http/errors.py +35 -0
  82. netbox_super_cli-1.0.0/nsc/http/retry.py +90 -0
  83. netbox_super_cli-1.0.0/nsc/model/__init__.py +23 -0
  84. netbox_super_cli-1.0.0/nsc/model/command_model.py +125 -0
  85. netbox_super_cli-1.0.0/nsc/output/__init__.py +1 -0
  86. netbox_super_cli-1.0.0/nsc/output/csv_.py +34 -0
  87. netbox_super_cli-1.0.0/nsc/output/errors.py +346 -0
  88. netbox_super_cli-1.0.0/nsc/output/explain.py +194 -0
  89. netbox_super_cli-1.0.0/nsc/output/flatten.py +25 -0
  90. netbox_super_cli-1.0.0/nsc/output/headers.py +9 -0
  91. netbox_super_cli-1.0.0/nsc/output/json_.py +21 -0
  92. netbox_super_cli-1.0.0/nsc/output/jsonl.py +21 -0
  93. netbox_super_cli-1.0.0/nsc/output/render.py +47 -0
  94. netbox_super_cli-1.0.0/nsc/output/table.py +50 -0
  95. netbox_super_cli-1.0.0/nsc/output/yaml_.py +28 -0
  96. netbox_super_cli-1.0.0/nsc/schema/__init__.py +1 -0
  97. netbox_super_cli-1.0.0/nsc/schema/hashing.py +24 -0
  98. netbox_super_cli-1.0.0/nsc/schema/loader.py +66 -0
  99. netbox_super_cli-1.0.0/nsc/schema/models.py +109 -0
  100. netbox_super_cli-1.0.0/nsc/schema/source.py +120 -0
  101. netbox_super_cli-1.0.0/nsc/schemas/__init__.py +1 -0
  102. netbox_super_cli-1.0.0/nsc/schemas/bundled/__init__.py +1 -0
  103. netbox_super_cli-1.0.0/nsc/schemas/bundled/manifest.yaml +5 -0
  104. netbox_super_cli-1.0.0/nsc/schemas/bundled/netbox-4.6.0-beta2.json.gz +0 -0
  105. netbox_super_cli-1.0.0/nsc/skill/__init__.py +35 -0
  106. netbox_super_cli-1.0.0/pyproject.toml +105 -0
  107. netbox_super_cli-1.0.0/scripts/gen_docs.py +267 -0
  108. netbox_super_cli-1.0.0/scripts/sync_agents_md.py +71 -0
  109. netbox_super_cli-1.0.0/skills/netbox-super-cli/SKILL.md +127 -0
  110. netbox_super_cli-1.0.0/tests/__init__.py +0 -0
  111. netbox_super_cli-1.0.0/tests/aliases/__init__.py +0 -0
  112. netbox_super_cli-1.0.0/tests/aliases/test_resolver.py +198 -0
  113. netbox_super_cli-1.0.0/tests/auth/__init__.py +0 -0
  114. netbox_super_cli-1.0.0/tests/auth/test_verify.py +84 -0
  115. netbox_super_cli-1.0.0/tests/benchmarks/__init__.py +0 -0
  116. netbox_super_cli-1.0.0/tests/benchmarks/test_startup.py +46 -0
  117. netbox_super_cli-1.0.0/tests/builder/__init__.py +0 -0
  118. netbox_super_cli-1.0.0/tests/builder/test_build.py +149 -0
  119. netbox_super_cli-1.0.0/tests/builder/test_default_columns.py +233 -0
  120. netbox_super_cli-1.0.0/tests/builder/test_redaction_marking.py +205 -0
  121. netbox_super_cli-1.0.0/tests/builder/test_request_body_shape.py +227 -0
  122. netbox_super_cli-1.0.0/tests/cache/__init__.py +0 -0
  123. netbox_super_cli-1.0.0/tests/cache/test_prune.py +253 -0
  124. netbox_super_cli-1.0.0/tests/cache/test_store.py +120 -0
  125. netbox_super_cli-1.0.0/tests/cli/__init__.py +0 -0
  126. netbox_super_cli-1.0.0/tests/cli/test_aliases_commands.py +322 -0
  127. netbox_super_cli-1.0.0/tests/cli/test_app_smoke.py +89 -0
  128. netbox_super_cli-1.0.0/tests/cli/test_cache_commands.py +129 -0
  129. netbox_super_cli-1.0.0/tests/cli/test_commands_dump.py +44 -0
  130. netbox_super_cli-1.0.0/tests/cli/test_completion_smoke.py +26 -0
  131. netbox_super_cli-1.0.0/tests/cli/test_config_commands.py +140 -0
  132. netbox_super_cli-1.0.0/tests/cli/test_explain.py +175 -0
  133. netbox_super_cli-1.0.0/tests/cli/test_handlers.py +198 -0
  134. netbox_super_cli-1.0.0/tests/cli/test_init_commands.py +83 -0
  135. netbox_super_cli-1.0.0/tests/cli/test_login_commands.py +159 -0
  136. netbox_super_cli-1.0.0/tests/cli/test_ndjson_input.py +154 -0
  137. netbox_super_cli-1.0.0/tests/cli/test_profiles_commands.py +174 -0
  138. netbox_super_cli-1.0.0/tests/cli/test_registration.py +338 -0
  139. netbox_super_cli-1.0.0/tests/cli/test_respx_integration.py +168 -0
  140. netbox_super_cli-1.0.0/tests/cli/test_runtime.py +153 -0
  141. netbox_super_cli-1.0.0/tests/cli/test_skill_commands.py +124 -0
  142. netbox_super_cli-1.0.0/tests/cli/test_stdin_sniffer.py +67 -0
  143. netbox_super_cli-1.0.0/tests/cli/test_writes_respx.py +597 -0
  144. netbox_super_cli-1.0.0/tests/cli/writes/__init__.py +0 -0
  145. netbox_super_cli-1.0.0/tests/cli/writes/test_apply.py +286 -0
  146. netbox_super_cli-1.0.0/tests/cli/writes/test_bulk.py +302 -0
  147. netbox_super_cli-1.0.0/tests/cli/writes/test_confirmation.py +87 -0
  148. netbox_super_cli-1.0.0/tests/cli/writes/test_handler_helpers.py +130 -0
  149. netbox_super_cli-1.0.0/tests/cli/writes/test_handlers_audit.py +51 -0
  150. netbox_super_cli-1.0.0/tests/cli/writes/test_input.py +179 -0
  151. netbox_super_cli-1.0.0/tests/cli/writes/test_preflight.py +226 -0
  152. netbox_super_cli-1.0.0/tests/config/__init__.py +0 -0
  153. netbox_super_cli-1.0.0/tests/config/test_loader.py +152 -0
  154. netbox_super_cli-1.0.0/tests/config/test_models.py +45 -0
  155. netbox_super_cli-1.0.0/tests/config/test_writer_atomicity.py +59 -0
  156. netbox_super_cli-1.0.0/tests/config/test_writer_dotted_paths.py +78 -0
  157. netbox_super_cli-1.0.0/tests/config/test_writer_roundtrip.py +31 -0
  158. netbox_super_cli-1.0.0/tests/conftest.py +41 -0
  159. netbox_super_cli-1.0.0/tests/e2e/README.md +128 -0
  160. netbox_super_cli-1.0.0/tests/e2e/__init__.py +1 -0
  161. netbox_super_cli-1.0.0/tests/e2e/conftest.py +176 -0
  162. netbox_super_cli-1.0.0/tests/e2e/docker-compose.yml +55 -0
  163. netbox_super_cli-1.0.0/tests/e2e/test_audit_redaction.py +83 -0
  164. netbox_super_cli-1.0.0/tests/e2e/test_auth_error_envelope.py +23 -0
  165. netbox_super_cli-1.0.0/tests/e2e/test_bulk_create_with_loop_fallback.py +87 -0
  166. netbox_super_cli-1.0.0/tests/e2e/test_full_cycle.py +164 -0
  167. netbox_super_cli-1.0.0/tests/e2e/test_login.py +134 -0
  168. netbox_super_cli-1.0.0/tests/e2e/test_ndjson.py +88 -0
  169. netbox_super_cli-1.0.0/tests/e2e/test_preflight_blocks_apply.py +35 -0
  170. netbox_super_cli-1.0.0/tests/e2e/test_validation_error_envelope_from_netbox.py +41 -0
  171. netbox_super_cli-1.0.0/tests/e2e/wait_for_netbox.sh +74 -0
  172. netbox_super_cli-1.0.0/tests/fixtures/profiles/single_profile.yaml +8 -0
  173. netbox_super_cli-1.0.0/tests/fixtures/responses/auth_401.json +1 -0
  174. netbox_super_cli-1.0.0/tests/fixtures/responses/circuits_providers_list.json +9 -0
  175. netbox_super_cli-1.0.0/tests/fixtures/responses/dcim_devices_get.json +1 -0
  176. netbox_super_cli-1.0.0/tests/fixtures/responses/dcim_devices_list_p1.json +9 -0
  177. netbox_super_cli-1.0.0/tests/fixtures/responses/dcim_devices_list_p2.json +8 -0
  178. netbox_super_cli-1.0.0/tests/http/__init__.py +0 -0
  179. netbox_super_cli-1.0.0/tests/http/test_audit.py +148 -0
  180. netbox_super_cli-1.0.0/tests/http/test_audit_redaction.py +110 -0
  181. netbox_super_cli-1.0.0/tests/http/test_client.py +384 -0
  182. netbox_super_cli-1.0.0/tests/http/test_client_redaction_threading.py +43 -0
  183. netbox_super_cli-1.0.0/tests/http/test_errors.py +31 -0
  184. netbox_super_cli-1.0.0/tests/http/test_retry.py +136 -0
  185. netbox_super_cli-1.0.0/tests/model/__init__.py +0 -0
  186. netbox_super_cli-1.0.0/tests/model/test_command_model.py +95 -0
  187. netbox_super_cli-1.0.0/tests/model/test_request_body_shape.py +89 -0
  188. netbox_super_cli-1.0.0/tests/output/__init__.py +0 -0
  189. netbox_super_cli-1.0.0/tests/output/test_csv.py +52 -0
  190. netbox_super_cli-1.0.0/tests/output/test_errors.py +252 -0
  191. netbox_super_cli-1.0.0/tests/output/test_errors_aliases.py +103 -0
  192. netbox_super_cli-1.0.0/tests/output/test_explain.py +80 -0
  193. netbox_super_cli-1.0.0/tests/output/test_flatten.py +34 -0
  194. netbox_super_cli-1.0.0/tests/output/test_input_error_envelope.py +46 -0
  195. netbox_super_cli-1.0.0/tests/output/test_json.py +25 -0
  196. netbox_super_cli-1.0.0/tests/output/test_jsonl.py +21 -0
  197. netbox_super_cli-1.0.0/tests/output/test_render_dispatch.py +69 -0
  198. netbox_super_cli-1.0.0/tests/output/test_table.py +59 -0
  199. netbox_super_cli-1.0.0/tests/output/test_yaml.py +33 -0
  200. netbox_super_cli-1.0.0/tests/schema/__init__.py +0 -0
  201. netbox_super_cli-1.0.0/tests/schema/test_hashing.py +31 -0
  202. netbox_super_cli-1.0.0/tests/schema/test_loader.py +81 -0
  203. netbox_super_cli-1.0.0/tests/schema/test_models.py +66 -0
  204. netbox_super_cli-1.0.0/tests/schema/test_source.py +159 -0
  205. netbox_super_cli-1.0.0/tests/scripts/__init__.py +0 -0
  206. netbox_super_cli-1.0.0/tests/scripts/test_gen_docs.py +95 -0
  207. netbox_super_cli-1.0.0/tests/skill/__init__.py +0 -0
  208. netbox_super_cli-1.0.0/tests/skill/test_bundle.py +66 -0
  209. netbox_super_cli-1.0.0/uv.lock +1191 -0
@@ -0,0 +1,35 @@
1
+ name: agents-md-sync
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - "CLAUDE.md"
7
+ - "AGENTS.md"
8
+ - "scripts/sync_agents_md.py"
9
+ - ".github/workflows/agents-md-sync.yml"
10
+ push:
11
+ branches:
12
+ - main
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ check:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v3
25
+ with:
26
+ enable-cache: true
27
+
28
+ - name: Install Python
29
+ run: uv python install 3.12
30
+
31
+ - name: Sync deps
32
+ run: uv sync --frozen
33
+
34
+ - name: Check AGENTS.md is in sync with CLAUDE.md
35
+ run: uv run python scripts/sync_agents_md.py --check
@@ -0,0 +1,23 @@
1
+ name: bench
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ startup-time:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: astral-sh/setup-uv@v4
15
+ - name: Install
16
+ run: uv sync --all-extras --dev
17
+ - name: Benchmark
18
+ run: NSC_BENCH=1 uv run pytest tests/benchmarks/ -v -s
19
+ continue-on-error: true
20
+ - name: Step summary
21
+ if: always()
22
+ run: |
23
+ echo "Bench job ran. See the previous step's stdout for the median timing." >> "$GITHUB_STEP_SUMMARY"
@@ -0,0 +1,55 @@
1
+ name: docs
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - "docs/**"
7
+ - "mkdocs.yml"
8
+ - "scripts/gen_docs.py"
9
+ - ".github/workflows/docs.yml"
10
+ push:
11
+ tags:
12
+ - "v*"
13
+
14
+ # Permissions for the deploy job (idempotent on the build-only PR job).
15
+ permissions:
16
+ contents: read
17
+ pages: write
18
+ id-token: write
19
+
20
+ # Only one in-flight deploy at a time; cancel queued ones.
21
+ concurrency:
22
+ group: pages
23
+ cancel-in-progress: false
24
+
25
+ jobs:
26
+ build:
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@v3
32
+ with:
33
+ enable-cache: true
34
+ - name: Install Python
35
+ run: uv python install 3.12
36
+ - name: Sync deps (runtime + docs)
37
+ run: uv sync --frozen --group docs
38
+ - name: Build site (strict)
39
+ run: uv run mkdocs build --strict
40
+ - name: Upload Pages artifact
41
+ if: github.ref_type == 'tag'
42
+ uses: actions/upload-pages-artifact@v3
43
+ with:
44
+ path: site
45
+
46
+ deploy:
47
+ needs: build
48
+ if: github.ref_type == 'tag'
49
+ runs-on: ubuntu-latest
50
+ environment:
51
+ name: github-pages
52
+ url: ${{ steps.deployment.outputs.page_url }}
53
+ steps:
54
+ - id: deployment
55
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,42 @@
1
+ name: e2e
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ paths:
8
+ - 'nsc/http/**'
9
+ - 'nsc/cli/handlers.py'
10
+ - 'nsc/cli/registration.py'
11
+ - 'nsc/cli/writes/**'
12
+ - 'nsc/output/explain.py'
13
+ - 'nsc/output/errors.py'
14
+ - 'tests/e2e/**'
15
+ - '.github/workflows/e2e.yml'
16
+ workflow_dispatch:
17
+
18
+ jobs:
19
+ e2e:
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 15
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: astral-sh/setup-uv@v3
25
+ - name: Sync deps
26
+ run: uv sync --frozen
27
+ - name: Start NetBox
28
+ run: docker compose -f tests/e2e/docker-compose.yml up -d
29
+ - name: Wait for NetBox
30
+ run: tests/e2e/wait_for_netbox.sh
31
+ - name: Run e2e tests
32
+ env:
33
+ NSC_E2E: '1'
34
+ NSC_URL: http://127.0.0.1:8080
35
+ NSC_TOKEN: '0123456789abcdef0123456789abcdef01234567'
36
+ run: uv run pytest tests/e2e/ -v
37
+ - name: Dump container logs on failure
38
+ if: failure()
39
+ run: docker compose -f tests/e2e/docker-compose.yml logs
40
+ - name: Stop NetBox
41
+ if: always()
42
+ run: docker compose -f tests/e2e/docker-compose.yml down -v
@@ -0,0 +1,56 @@
1
+ name: lint
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ lint:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - name: Install uv
14
+ uses: astral-sh/setup-uv@v3
15
+ with:
16
+ enable-cache: true
17
+ - name: Install Python
18
+ run: uv python install 3.12
19
+ - name: Sync deps
20
+ run: uv sync --frozen
21
+ - name: Ruff check
22
+ run: uv run ruff check nsc tests scripts
23
+ - name: Ruff format
24
+ run: uv run ruff format --check nsc tests scripts
25
+ - name: Mypy strict
26
+ run: uv run mypy --strict nsc scripts
27
+
28
+ docs-reference-fresh:
29
+ runs-on: ubuntu-latest
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - name: Install uv
33
+ uses: astral-sh/setup-uv@v3
34
+ with:
35
+ enable-cache: true
36
+ - name: Install Python
37
+ run: uv python install 3.12
38
+ - name: Sync deps (runtime + docs — gen_docs needs both)
39
+ run: uv sync --frozen --group docs
40
+ - name: Verify auto-generated reference pages are up to date
41
+ run: uv run python scripts/gen_docs.py --check
42
+
43
+ agents-md-fresh:
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+ - name: Install uv
48
+ uses: astral-sh/setup-uv@v3
49
+ with:
50
+ enable-cache: true
51
+ - name: Install Python
52
+ run: uv python install 3.12
53
+ - name: Sync deps
54
+ run: uv sync --frozen
55
+ - name: Check AGENTS.md is in sync with CLAUDE.md
56
+ run: uv run python scripts/sync_agents_md.py --check
@@ -0,0 +1,163 @@
1
+ name: release
2
+
3
+ # Tag-only trigger. NEVER on pull_request or branch push — the trusted
4
+ # publisher is bound to this workflow filename + the v* tag pattern, so a
5
+ # stray dispatch on main would either fail authentication (good) or, if
6
+ # someone misconfigured PyPI, publish a bogus release (very bad).
7
+ on:
8
+ push:
9
+ tags:
10
+ # Release versions: v<digit><anything>.<digit><anything>.<digit><anything>.
11
+ # GitHub's tag filter uses fnmatch (not regex), so the leading [0-9]
12
+ # excludes vfoobar / vNEXT entirely, but the unbounded `*` after each
13
+ # digit class also matches v1.0.0-rc1 and v1.0.0.1. Those slip through
14
+ # the trigger and are caught by the version-match step below — the
15
+ # filter narrows the obvious garbage; the version-match step is the
16
+ # actual gate.
17
+ - "v[0-9]*.[0-9]*.[0-9]*"
18
+
19
+ # Trusted publishing requires id-token: write at the workflow level so the
20
+ # OIDC token issued to pypa/gh-action-pypi-publish is signed.
21
+ permissions:
22
+ contents: write # gh release create
23
+ id-token: write # PyPI trusted publishing OIDC
24
+
25
+ # One in-flight release at a time; queue rather than cancel so a fast
26
+ # re-tag never overlaps the previous publish.
27
+ concurrency:
28
+ group: release
29
+ cancel-in-progress: false
30
+
31
+ jobs:
32
+ release:
33
+ runs-on: ubuntu-latest
34
+ # PyPI trusted publishing reads the workflow filename + repo + tag-ref
35
+ # from the OIDC claim. No environment is required for PyPI; we
36
+ # explicitly do NOT set an `environment:` block here — adding one would
37
+ # change the OIDC subject claim and break the trusted-publisher binding
38
+ # the user registered against the no-environment form (per §Pre-tag
39
+ # prerequisites).
40
+ steps:
41
+ - name: Checkout (full history for changelog notes)
42
+ uses: actions/checkout@v4
43
+ with:
44
+ fetch-depth: 0
45
+
46
+ - name: Verify tag is on main
47
+ run: |
48
+ set -euo pipefail
49
+ # Trusted publishing's OIDC claim is bound to the workflow filename
50
+ # and tag ref — it does NOT enforce that the tagged commit is on
51
+ # main. Tags pushed from a topic branch (or any unmerged commit)
52
+ # would otherwise publish from un-reviewed code. This guard rejects
53
+ # any tag whose commit is not reachable from origin/main.
54
+ # actions/checkout@v4 with fetch-depth: 0 fetches the tag's full
55
+ # history but does not populate origin/main as a ref locally; we
56
+ # fetch it explicitly here.
57
+ git fetch origin main
58
+ if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
59
+ echo "Tag $GITHUB_REF_NAME points at $GITHUB_SHA which is not reachable from main." >&2
60
+ exit 1
61
+ fi
62
+ echo "Tag $GITHUB_REF_NAME ($GITHUB_SHA) is on main."
63
+
64
+ - name: Install uv
65
+ uses: astral-sh/setup-uv@v3
66
+ with:
67
+ enable-cache: true
68
+
69
+ - name: Install Python
70
+ run: uv python install 3.12
71
+
72
+ - name: Sync runtime deps (so the next step can import nsc._version)
73
+ run: uv sync --frozen
74
+
75
+ - name: Verify tag matches package version
76
+ run: |
77
+ set -euo pipefail
78
+ tag_version="${GITHUB_REF_NAME#v}"
79
+ pkg_version="$(uv run python -c 'from nsc._version import __version__; print(__version__)')"
80
+ if [ "$tag_version" != "$pkg_version" ]; then
81
+ echo "Tag $GITHUB_REF_NAME -> version $tag_version does not match nsc/_version.py $pkg_version" >&2
82
+ exit 1
83
+ fi
84
+ echo "Tag $GITHUB_REF_NAME matches package version $pkg_version"
85
+
86
+ - name: Build sdist + wheel
87
+ run: |
88
+ rm -rf dist
89
+ uv build --out-dir dist
90
+ ls -la dist/
91
+
92
+ - name: Verify wheel filename matches tag
93
+ run: |
94
+ set -euo pipefail
95
+ tag_version="${GITHUB_REF_NAME#v}"
96
+ if ! ls "dist/netbox_super_cli-${tag_version}-py3-none-any.whl" >/dev/null 2>&1; then
97
+ echo "Wheel for ${tag_version} not found in dist/" >&2
98
+ ls dist/ >&2
99
+ exit 1
100
+ fi
101
+ if ! ls "dist/netbox_super_cli-${tag_version}.tar.gz" >/dev/null 2>&1; then
102
+ echo "Sdist for ${tag_version} not found in dist/" >&2
103
+ ls dist/ >&2
104
+ exit 1
105
+ fi
106
+
107
+ - name: Publish to PyPI (trusted publishing)
108
+ uses: pypa/gh-action-pypi-publish@release/v1
109
+ with:
110
+ # No `password:` — trusted publishing uses the OIDC token bound
111
+ # to id-token: write above. PyPI must have a Trusted Publisher
112
+ # registered for this repo + workflow + (no) environment before
113
+ # this step runs (see §Pre-tag prerequisites in the 5d plan).
114
+ packages-dir: dist
115
+ # Print which artifacts were uploaded; helps diagnose mismatches.
116
+ verbose: true
117
+ # Don't fail if a re-tag attempts to re-upload an existing
118
+ # filename: the action calls PyPI's filename-exists check and
119
+ # silently skips the upload (it does NOT compare byte content
120
+ # of the existing vs. would-be-uploaded artifact). PyPI itself
121
+ # treats version artifacts as immutable, so the file already on
122
+ # PyPI is the canonical bytes for that version. A re-tag that
123
+ # builds different bytes for the same version would publish
124
+ # those bytes only to the GitHub Release, not to PyPI — diverging
125
+ # the two. Avoid that by never re-tagging an already-published
126
+ # version; bump to the next patch version instead.
127
+ skip-existing: true
128
+
129
+ - name: Extract changelog section for release notes
130
+ id: notes
131
+ run: |
132
+ set -euo pipefail
133
+ tag_version="${GITHUB_REF_NAME#v}"
134
+ # Match `## v<ver><space>...` and capture until the next `## `
135
+ # heading. The trailing space after <ver> in the regex is what
136
+ # disambiguates `v1.0` from `v1.0.1`; what follows (typically an
137
+ # em-dash, date, sub-phase name) is intentionally unconstrained.
138
+ awk -v ver="$tag_version" '
139
+ $0 ~ "^## v" ver " " { capture=1; print; next }
140
+ capture && /^## / { exit }
141
+ capture { print }
142
+ ' CHANGELOG.md > /tmp/release-notes.md
143
+ if [ ! -s /tmp/release-notes.md ]; then
144
+ echo "Release notes for v${tag_version} not found in CHANGELOG.md" >&2
145
+ exit 1
146
+ fi
147
+ echo "Extracted $(wc -l < /tmp/release-notes.md) lines of release notes."
148
+
149
+ - name: Create GitHub Release
150
+ env:
151
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
152
+ run: |
153
+ set -euo pipefail
154
+ tag_version="${GITHUB_REF_NAME#v}"
155
+ # Use exact filenames (the verify-wheel-filename step proved both
156
+ # exist) rather than glob-expanding `dist/*.whl dist/*.tar.gz` —
157
+ # that glob would silently attach any stray artifact a future
158
+ # build step might drop into dist/.
159
+ gh release create "$GITHUB_REF_NAME" \
160
+ --title "$GITHUB_REF_NAME" \
161
+ --notes-file /tmp/release-notes.md \
162
+ "dist/netbox_super_cli-${tag_version}-py3-none-any.whl" \
163
+ "dist/netbox_super_cli-${tag_version}.tar.gz"
@@ -0,0 +1,27 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ${{ matrix.os }}
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os: [ubuntu-latest, macos-latest]
15
+ python: ["3.12", "3.13"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v3
20
+ with:
21
+ enable-cache: true
22
+ - name: Install Python
23
+ run: uv python install ${{ matrix.python }}
24
+ - name: Sync deps
25
+ run: uv sync --frozen
26
+ - name: Run tests
27
+ run: uv run pytest -v
@@ -0,0 +1,37 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ build/
9
+ dist/
10
+ *.egg
11
+
12
+ # venv
13
+ .venv/
14
+ venv/
15
+
16
+ # uv
17
+ # (uv.lock IS committed)
18
+
19
+ # IDE
20
+ .idea/
21
+ .vscode/
22
+ *.swp
23
+ .DS_Store
24
+
25
+ # nsc local state (must never be committed)
26
+ .nsc/
27
+
28
+ # Secrets
29
+ .env
30
+ .env.*
31
+ *.env
32
+
33
+ # Local-only design notes (kept out of git history)
34
+ docs/superpowers/
35
+
36
+ # mkdocs
37
+ site/
@@ -0,0 +1,35 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.6.0
4
+ hooks:
5
+ - id: end-of-file-fixer
6
+ - id: trailing-whitespace
7
+ - id: check-yaml
8
+ - id: check-toml
9
+ - id: check-merge-conflict
10
+ - id: check-added-large-files
11
+ # 2 MB is enough now that bundled schemas are gzipped.
12
+ args: [--maxkb=2048]
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.15.12
15
+ hooks:
16
+ - id: ruff
17
+ args: [--fix]
18
+ - id: ruff-format
19
+ - repo: https://github.com/pre-commit/mirrors-mypy
20
+ rev: v1.20.2
21
+ hooks:
22
+ - id: mypy
23
+ # Keep this list in sync with [project.dependencies] in pyproject.toml,
24
+ # plus type stubs for any third-party packages used in scripts/.
25
+ additional_dependencies:
26
+ - pydantic>=2.7
27
+ - typer>=0.12
28
+ - rich>=13.7
29
+ - httpx>=0.27
30
+ - "ruamel.yaml>=0.18"
31
+ - structlog>=24.1
32
+ - platformdirs>=4.2
33
+ - types-PyYAML>=6.0
34
+ args: [--strict]
35
+ files: ^(nsc|scripts)/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,45 @@
1
+ <!--
2
+ AGENTS.md is auto-generated from CLAUDE.md by scripts/sync_agents_md.py.
3
+ Do NOT edit this file directly — edit CLAUDE.md and run
4
+ `uv run python scripts/sync_agents_md.py` to regenerate. CI fails the PR
5
+ if this file drifts from CLAUDE.md (see .github/workflows/lint.yml).
6
+ -->
7
+
8
+ # Working on netbox-super-cli (for AI agents)
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` (added in Phase 5).
11
+
12
+ ## Architecture cheat sheet
13
+
14
+ - `nsc/schema/` — parses an OpenAPI document into Pydantic models. Knows nothing about CLIs.
15
+ - `nsc/model/` — the normalized command tree (data only, framework-free). The "brain".
16
+ - `nsc/builder/` — converts a parsed schema into a `CommandModel`.
17
+ - `nsc/cli/` — the Typer app; consumes a `CommandModel`.
18
+ - `nsc/http/` — thin httpx wrapper (Phase 2+).
19
+ - `nsc/output/` — formatters (Phase 2+).
20
+ - `nsc/config/` — config loader + Pydantic models (Phase 1 has a minimal version).
21
+ - `nsc/cache/` — disk cache for generated command-models.
22
+ - `nsc/schemas/bundled/` — versioned NetBox OpenAPI snapshots, fallback when offline.
23
+
24
+ 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.
25
+
26
+ ## Common commands
27
+
28
+ - `just sync` — install/refresh deps.
29
+ - `just test` — run all tests.
30
+ - `just lint` — ruff + mypy --strict.
31
+ - `just fix` — auto-fix ruff issues.
32
+ - `just nsc <args>` — run the local CLI.
33
+
34
+ ## Conventions
35
+
36
+ - Python 3.12+, full type annotations, `mypy --strict`.
37
+ - Pydantic v2 for all structured data.
38
+ - Conventional Commits.
39
+ - TDD: write the failing test first.
40
+ - No comments on what code does; only on non-obvious *why*.
41
+
42
+ ## Where the design lives
43
+
44
+ - `docs/superpowers/specs/2026-04-30-netbox-super-cli-design.md` — the full design.
45
+ - `docs/superpowers/plans/` — implementation plans, one per phase.