nerftools 0.3.2__tar.gz → 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 (153) hide show
  1. {nerftools-0.3.2 → nerftools-1.0.0}/.cspell.json +2 -0
  2. {nerftools-0.3.2 → nerftools-1.0.0}/.github/workflows/release-please.yml +8 -9
  3. {nerftools-0.3.2 → nerftools-1.0.0}/.gitignore +3 -0
  4. nerftools-1.0.0/.release-please-manifest.json +3 -0
  5. {nerftools-0.3.2 → nerftools-1.0.0}/CHANGELOG.md +22 -0
  6. {nerftools-0.3.2 → nerftools-1.0.0}/CONTRIBUTING.md +3 -3
  7. {nerftools-0.3.2 → nerftools-1.0.0}/PKG-INFO +1 -1
  8. {nerftools-0.3.2 → nerftools-1.0.0}/README.md +16 -6
  9. nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/frd.md +401 -0
  10. nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/hla.md +530 -0
  11. nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/locked.md +9 -0
  12. nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/manifest-spec.md +1032 -0
  13. nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/plan.md +227 -0
  14. nerftools-1.0.0/nerf.yaml +22 -0
  15. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/__init__.py +0 -1
  16. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/cli.py +49 -18
  17. nerftools-1.0.0/nerftools/config.py +391 -0
  18. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/formats.py +1 -1
  19. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/.claude-plugin/plugin.json +1 -1
  20. {nerftools-0.3.2 → nerftools-1.0.0}/pyproject.toml +1 -1
  21. {nerftools-0.3.2 → nerftools-1.0.0}/release-please-config.json +1 -1
  22. nerftools-1.0.0/tests/test_config.py +297 -0
  23. {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_formats.py +2 -2
  24. {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_nerfctl.py +1 -15
  25. {nerftools-0.3.2 → nerftools-1.0.0}/uv.lock +1 -1
  26. nerftools-0.3.2/.release-please-manifest.json +0 -3
  27. nerftools-0.3.2/nerf-plugin.yaml +0 -21
  28. nerftools-0.3.2/nerftools/nerfctl/claude/install-plugin.sh +0 -57
  29. nerftools-0.3.2/nerftools/plugin_meta.py +0 -200
  30. nerftools-0.3.2/out/claude-plugin/scripts/nerfctl-install-plugin +0 -57
  31. nerftools-0.3.2/tests/test_plugin_meta.py +0 -187
  32. {nerftools-0.3.2 → nerftools-1.0.0}/.claude-plugin/marketplace.json +0 -0
  33. {nerftools-0.3.2 → nerftools-1.0.0}/.editorconfig +0 -0
  34. {nerftools-0.3.2 → nerftools-1.0.0}/.github/workflows/ci.yml +0 -0
  35. {nerftools-0.3.2 → nerftools-1.0.0}/.github/workflows/release.yml +0 -0
  36. {nerftools-0.3.2 → nerftools-1.0.0}/.markdownlint-cli2.jsonc +0 -0
  37. {nerftools-0.3.2 → nerftools-1.0.0}/.prettierrc +0 -0
  38. {nerftools-0.3.2 → nerftools-1.0.0}/.python-version +0 -0
  39. {nerftools-0.3.2 → nerftools-1.0.0}/LICENSE +0 -0
  40. {nerftools-0.3.2 → nerftools-1.0.0}/docs/nerf-manifest.md +0 -0
  41. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/builder.py +0 -0
  42. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/README.md +0 -0
  43. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/__init__.py +0 -0
  44. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/az-boards.yaml +0 -0
  45. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/az-pipelines.yaml +0 -0
  46. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/az-repos.yaml +0 -0
  47. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/gh.yaml +0 -0
  48. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/git.yaml +0 -0
  49. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/nx.yaml +0 -0
  50. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/pkgrun.yaml +0 -0
  51. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/stdutils.yaml +0 -0
  52. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/tg.yaml +0 -0
  53. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/uv.yaml +0 -0
  54. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/manifest.py +0 -0
  55. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/__init__.py +0 -0
  56. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-allow.sh +0 -0
  57. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-by-threat.sh +0 -0
  58. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-deny.sh +0 -0
  59. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-list.sh +0 -0
  60. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-reset.sh +0 -0
  61. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/rendering.py +0 -0
  62. {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/skill.py +0 -0
  63. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-allow +0 -0
  64. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-by-threat +0 -0
  65. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-deny +0 -0
  66. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-list +0 -0
  67. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-reset +0 -0
  68. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/SKILL.md +0 -0
  69. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-mywi-comment +0 -0
  70. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-mywi-show +0 -0
  71. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-add-parent +0 -0
  72. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-comment +0 -0
  73. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-create +0 -0
  74. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-list +0 -0
  75. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-show +0 -0
  76. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-update +0 -0
  77. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-pipelines/SKILL.md +0 -0
  78. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-pipelines/scripts/nerf-az-pipelines-check +0 -0
  79. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-pipelines/scripts/nerf-az-pipelines-list +0 -0
  80. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-pipelines/scripts/nerf-az-pipelines-runs +0 -0
  81. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/SKILL.md +0 -0
  82. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/scripts/nerf-az-repos-pr-comments +0 -0
  83. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/scripts/nerf-az-repos-pr-create +0 -0
  84. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/scripts/nerf-az-repos-pr-list +0 -0
  85. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/scripts/nerf-az-repos-pr-show +0 -0
  86. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/SKILL.md +0 -0
  87. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-issue-comment +0 -0
  88. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-issue-create +0 -0
  89. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-issue-list +0 -0
  90. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-issue-view +0 -0
  91. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-comment +0 -0
  92. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-create +0 -0
  93. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-diff +0 -0
  94. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-list +0 -0
  95. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-review-comments +0 -0
  96. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-view +0 -0
  97. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-run-list +0 -0
  98. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-run-view +0 -0
  99. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/SKILL.md +0 -0
  100. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-add +0 -0
  101. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-commit +0 -0
  102. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-commit-amend +0 -0
  103. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-fetch +0 -0
  104. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-log +0 -0
  105. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-pull +0 -0
  106. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-push-branch +0 -0
  107. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-push-main +0 -0
  108. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-reset-hard-last +0 -0
  109. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-revert +0 -0
  110. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-tag +0 -0
  111. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/SKILL.md +0 -0
  112. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-affected +0 -0
  113. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-graph +0 -0
  114. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-reset +0 -0
  115. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-run +0 -0
  116. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-show-project +0 -0
  117. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-show-projects +0 -0
  118. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-pkgrun/SKILL.md +0 -0
  119. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-pkgrun/scripts/nerf-pkgrun-cspell +0 -0
  120. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-pkgrun/scripts/nerf-pkgrun-markdownlint +0 -0
  121. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-pkgrun/scripts/nerf-pkgrun-prettier +0 -0
  122. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/SKILL.md +0 -0
  123. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/scripts/nerf-find +0 -0
  124. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/scripts/nerf-find-cwd +0 -0
  125. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/scripts/nerf-grep +0 -0
  126. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/scripts/nerf-grep-recursive-cwd +0 -0
  127. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/SKILL.md +0 -0
  128. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-fmt +0 -0
  129. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-fmt-all +0 -0
  130. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-init +0 -0
  131. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-init-all +0 -0
  132. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-output +0 -0
  133. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-output-all +0 -0
  134. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-plan +0 -0
  135. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-plan-all +0 -0
  136. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-validate +0 -0
  137. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-validate-all +0 -0
  138. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/SKILL.md +0 -0
  139. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/scripts/nerf-uv-mypy +0 -0
  140. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/scripts/nerf-uv-pytest +0 -0
  141. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/scripts/nerf-uv-ruff-check +0 -0
  142. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/scripts/nerf-uv-ruff-fix +0 -0
  143. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-allow/SKILL.md +0 -0
  144. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-by-threat/SKILL.md +0 -0
  145. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-deny/SKILL.md +0 -0
  146. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-list/SKILL.md +0 -0
  147. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-reset/SKILL.md +0 -0
  148. {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerftools/SKILL.md +0 -0
  149. {nerftools-0.3.2 → nerftools-1.0.0}/pypi-dist/.gitignore +0 -0
  150. {nerftools-0.3.2 → nerftools-1.0.0}/tests/__init__.py +0 -0
  151. {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_builder.py +0 -0
  152. {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_manifest.py +0 -0
  153. {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_skill.py +0 -0
@@ -10,6 +10,7 @@
10
10
  "defanged",
11
11
  "elif",
12
12
  "esac",
13
+ "footguns",
13
14
  "execdir",
14
15
  "exitcode",
15
16
  "frontmatter",
@@ -39,6 +40,7 @@
39
40
  "stdutils",
40
41
  "subshell",
41
42
  "tmpl",
43
+ "typer",
42
44
  "upgen",
43
45
  "uppercased",
44
46
  "usecases",
@@ -13,12 +13,11 @@ jobs:
13
13
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
14
14
  outputs:
15
15
  pr_branch: ${{ steps.extract.outputs.pr_branch }}
16
- app_token: ${{ steps.app-token.outputs.token }}
17
16
  steps:
18
17
  # Use a GitHub App token so the release PR (and any subsequent pushes
19
18
  # to its branch) trigger CI workflows. The default GITHUB_TOKEN creates
20
19
  # events as github-actions[bot], which GitHub won't run workflows for.
21
- - uses: actions/create-github-app-token@v2
20
+ - uses: actions/create-github-app-token@v3
22
21
  id: app-token
23
22
  with:
24
23
  app-id: ${{ vars.RELEASE_APP_ID }}
@@ -43,7 +42,7 @@ jobs:
43
42
  if: needs.release-please.outputs.pr_branch
44
43
  runs-on: ubuntu-latest
45
44
  steps:
46
- - uses: actions/create-github-app-token@v2
45
+ - uses: actions/create-github-app-token@v3
47
46
  id: app-token
48
47
  with:
49
48
  app-id: ${{ vars.RELEASE_APP_ID }}
@@ -59,19 +58,19 @@ jobs:
59
58
  with:
60
59
  enable-cache: true
61
60
 
62
- - name: Install dependencies
63
- run: uv sync --frozen
61
+ - name: Sync lockfile and install dependencies
62
+ run: uv sync
64
63
 
65
64
  - name: Regenerate Claude Code plugin
66
- run: uv run nerf generate --target claude-plugin --plugin-config nerf-plugin.yaml --outdir ./out/claude-plugin
65
+ run: uv run nerf generate --target claude-plugin -c nerf.yaml --outdir ./out/claude-plugin
67
66
 
68
- - name: Commit updated plugin
67
+ - name: Commit updated plugin and lockfile
69
68
  run: |
70
69
  git config user.name "nerftools-release[bot]"
71
70
  git config user.email "nerftools-release[bot]@users.noreply.github.com"
72
- git add out/claude-plugin/
71
+ git add out/claude-plugin/ uv.lock
73
72
  if git diff --cached --quiet; then
74
- echo "No changes to plugin output"
73
+ echo "No changes to plugin output or lockfile"
75
74
  else
76
75
  git commit -m "build: regenerate Claude Code plugin for release"
77
76
  git push
@@ -40,3 +40,6 @@ Thumbs.db
40
40
  .pytest_cache/
41
41
  htmlcov/
42
42
  .coverage
43
+
44
+ # tmuxinator config is personal
45
+ .tmuxinator.yml
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.0.0"
3
+ }
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.0](https://github.com/WayfarerLabs/nerftools/compare/v0.3.2...v1.0.0) (2026-04-19)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * `--plugin-config` and `--prefix` flags are removed. Use `-c <path>` to pass a config file. `--prefix` is now configured via `defaults.prefix` in the config file.
9
+
10
+ ### Features
11
+
12
+ * introduce optional config file, drop --plugin-config and install script ([f6a1ab9](https://github.com/WayfarerLabs/nerftools/commit/f6a1ab95f7f1e22fc84d6fea111390db38ef6ebb))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * address review feedback from Copilot ([60f4bc5](https://github.com/WayfarerLabs/nerftools/commit/60f4bc53ca87607b40873554a48ba7984a7b6c9e))
18
+ * **ci:** sync uv.lock during release to prevent version drift ([6eef9b8](https://github.com/WayfarerLabs/nerftools/commit/6eef9b8d6dfd2ccc0646cca71bdcc95fff779f48))
19
+
20
+
21
+ ### Documentation
22
+
23
+ * **sdd:** capture the refactor sdd as it defined the fundamental structure ([5c7cad4](https://github.com/WayfarerLabs/nerftools/commit/5c7cad4248c3320cd7d67848d4963376dd8f24ee))
24
+
3
25
  ## [0.3.2](https://github.com/WayfarerLabs/nerftools/compare/v0.3.1...v0.3.2) (2026-04-14)
4
26
 
5
27
 
@@ -38,7 +38,7 @@ After changing manifests or plugin generation code, you can rebuild the pre-buil
38
38
  to preview the output:
39
39
 
40
40
  ```bash
41
- uv run nerf generate --target claude-plugin --plugin-config nerf-plugin.yaml --outdir ./out/claude-plugin
41
+ uv run nerf generate --target claude-plugin -c nerf.yaml --outdir ./out/claude-plugin
42
42
  ```
43
43
 
44
44
  You don't need to commit the regenerated `out/` -- CI regenerates it as part of the release PR.
@@ -55,7 +55,7 @@ and conventional commits. No one manually bumps versions or creates tags.
55
55
  2. The `release-please` workflow keeps an open PR titled `chore(main): release X.Y.Z` that
56
56
  accumulates changes. On each push to `main` it:
57
57
  - Computes the next version from the conventional commits since the last release.
58
- - Updates the version in `pyproject.toml`, `nerf-plugin.yaml`, and `.release-please-manifest.json`.
58
+ - Updates the version in `pyproject.toml`, `nerf.yaml`, and `.release-please-manifest.json`.
59
59
  - Updates `CHANGELOG.md`.
60
60
  - Regenerates `out/claude-plugin/` with the new version baked into `plugin.json`.
61
61
  3. When you're ready to release, merge the release PR.
@@ -75,7 +75,7 @@ and conventional commits. No one manually bumps versions or creates tags.
75
75
  - Don't create tags by hand. `release-please` creates tags on merge of the release PR.
76
76
  - Don't edit `CHANGELOG.md` by hand. It's regenerated from commit messages.
77
77
  - Don't edit `out/claude-plugin/` by hand. Edit the source (`nerftools/`, manifests, or
78
- `nerf-plugin.yaml`) and the release PR will regenerate it.
78
+ `nerf.yaml`) and the release PR will regenerate it.
79
79
 
80
80
  ### Breaking changes
81
81
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nerftools
3
- Version: 0.3.2
3
+ Version: 1.0.0
4
4
  Summary: Define and generate nerf tools: limited-scope wrappers for common CLI utilities that allow for fine-grained control over agent execution
5
5
  Project-URL: Homepage, https://github.com/WayfarerLabs/nerftools
6
6
  Project-URL: Repository, https://github.com/WayfarerLabs/nerftools
@@ -100,14 +100,23 @@ uv run nerf generate --target bin --outdir ./bin
100
100
  # Generate rulesync skills
101
101
  uv run nerf generate --target skills --outdir ./skills
102
102
 
103
- # Generate a Claude Code plugin (requires a plugin metadata config)
104
- uv run nerf generate --target claude-plugin \
105
- --plugin-config nerf-plugin.yaml \
106
- --outdir ./claude-plugin
103
+ # Generate a Claude Code plugin (uses built-in defaults if no config)
104
+ uv run nerf generate --target claude-plugin --outdir ./claude-plugin
105
+
106
+ # Generate a Claude Code plugin with custom identity
107
+ uv run nerf generate --target claude-plugin -c nerf.yaml --outdir ./claude-plugin
107
108
  ```
108
109
 
109
- Plugin metadata is sourced from an external file to make it easy for teams to personalize the output
110
- plugin for their needs. See [nerf-plugin.yaml](nerf-plugin.yaml) for the plugin metadata format.
110
+ Package identity and per-target settings live in an optional config file passed via `-c <path>`.
111
+ When omitted, sensible defaults produce an installable plugin. See [nerf.yaml](nerf.yaml) for an
112
+ example.
113
+
114
+ To install the generated plugin:
115
+
116
+ ```bash
117
+ claude plugin marketplace add <plugin-dir>
118
+ claude plugin install <plugin-name>@<marketplace-name>
119
+ ```
111
120
 
112
121
  ## Default Manifests/Packages
113
122
 
@@ -183,6 +192,7 @@ nerftools/ Python package
183
192
  rendering.py Shared display helpers (maps-to, usage tokens)
184
193
  skill.py Rulesync skill generation
185
194
  formats.py Claude Code plugin builder
195
+ config.py Config loader, plugin metadata, defaults resolution
186
196
  cli.py CLI (validate + generate)
187
197
  nerfctl/claude/ Grant management shell scripts
188
198
  default_manifests/ Default tool package manifests (YAML)
@@ -0,0 +1,401 @@
1
+ # Nerf Tools v1 Manifest -- Functional Requirements
2
+
3
+ ## Problem Statement
4
+
5
+ The original nerf tools system works well for its original scope -- wrapping single commands with
6
+ explicit parameter validation. But three gaps have emerged:
7
+
8
+ 1. **Limited execution models.** The original design only supports wrapping a single command with
9
+ explicitly-defined parameters. There is no way to pass arguments through to an underlying tool
10
+ with a deny-list (e.g. `find` without `-exec`), and no way to define a tool with custom
11
+ multi-step logic.
12
+
13
+ 2. **Flat CLI interface.** The current CLI has separate `build` and `skill` commands that each
14
+ produce one output type. The `--install-nerfctl` flag is bolted onto `build`. As output targets
15
+ grow (bin scripts, skills, claude plugin), the interface needs restructuring around `validate`
16
+ and `generate` with pluggable targets.
17
+
18
+ 3. **No versioning or risk metadata.** Manifests have no version field, so there is no way to evolve
19
+ the spec while preserving backwards compatibility. Tools have no risk classification, so
20
+ permissions must be granted per-tool rather than by category.
21
+
22
+ 4. **Loose definition of args vs flags vs ...** The current manifest format does not clearly
23
+ distinguish between positional arguments, named flags, and other types of parameters. This opens
24
+ the door to ambiguity in how tokens are interpreted and potential security problems.
25
+
26
+ ## Goals
27
+
28
+ - Support three execution modes: **template** (explicit params + command template), **passthrough**
29
+ (deny-list + forward), and **script** (inline bash).
30
+ - Provide a clean CLI with `validate` and `generate` commands, where `generate` supports named
31
+ targets.
32
+ - Version manifests so the spec can evolve without breaking existing tools.
33
+ - Attach a threat profile to each tool so operators can grant permissions by risk scope rather than
34
+ enumerating every tool.
35
+
36
+ ## Requirements
37
+
38
+ ### R1: Parameter terminology
39
+
40
+ The new manifest replaces `flags` and `args` with three precise parameter types:
41
+
42
+ | New term | Old equivalent | What it is |
43
+ | ------------ | ---------------------------- | ---------------------- |
44
+ | **switch** | `flags` with `boolean: true` | Boolean flag, no value |
45
+ | **option** | `flags` without `boolean` | Named flag + value |
46
+ | **argument** | `args` | Positional value |
47
+
48
+ A **token** is a single raw element of `$@` — the unclassified unit before parsing. A **switch**
49
+ consumes one token. An **option** consumes two tokens: the **option flag** and the **option value**.
50
+ An **argument** consumes one token.
51
+
52
+ The invocation order is enforced: switches and options must come before arguments. The generated
53
+ parser stops consuming flags at the first non-flag token (or `--`).
54
+
55
+ ```text
56
+ nerf-tool [switches] [options] [--] <arguments...>
57
+ ```
58
+
59
+ In passthrough mode, tokens are never classified — they are scanned against deny rules and forwarded
60
+ as-is. **Prefix tokens** and **suffix tokens** are static tokens hardcoded before and after the
61
+ user's tokens.
62
+
63
+ ### R2: Three execution modes
64
+
65
+ Each tool defines exactly one execution mode:
66
+
67
+ - **template** -- Build a command from an explicit template with `{{param}}` placeholders. An
68
+ explicit allow list of parameters (switches, options, arguments) are parsed, validated, and
69
+ substituted. The final argument may be variadic, allowing the template to accept an arbitrary
70
+ number of trailing positional arguments from the user if appropriate for the underlying tool. This
71
+ mode is generally preferred when the underlying command has a well-defined set of parameters that
72
+ can be safely enumerated and substituted, as it allows for precise control over what the user can
73
+ pass and reduces the risk of injection or misuse.
74
+
75
+ - **passthrough** -- Forward all tokens to an underlying command after scanning them against a deny
76
+ list of glob patterns. Importantly, this mode operates entirely on a token basis. No attempt is
77
+ made to classify tokens as switches, options, or arguments is made. Every token is evaluated
78
+ against the deny list individually, and if it passes, it is forwarded to the underlying command
79
+ exactly as provided by the user. Prefix and suffix tokens allow for static tokens to be prepended
80
+ or appended to the user's token stream, enabling the tool to inject fixed arguments before or
81
+ after the user-supplied input without classifying or modifying the user's tokens. This mode is
82
+ provided for situations where an underlying tool has many parameters and only a few options or
83
+ switches need to be restricted (e.g. `exec` and similar for `find`).
84
+
85
+ - **script** -- Run an inline bash script. Parameters are parsed and available as shell variables,
86
+ but the script body is fully custom. As the most complex, this mode should only be used when the
87
+ underlying command does not fit into the other modes.
88
+
89
+ While it might be possible to combine aspects of template and passthrough modes into a hybrid mode,
90
+ we keep them separate and simple to avoid footguns and other security issues. The goal is that each
91
+ of these modes should be easy to reason about within its particular model.
92
+
93
+ All modes support guards (pre-flight checks) and pre hooks (setup logic that runs before main
94
+ execution).
95
+
96
+ ### R3: Pre hooks
97
+
98
+ Tools can define a `pre` script that runs before the main execution. Pre hooks are wrapped in a
99
+ shell function so that:
100
+
101
+ - `return 1` aborts with a fallback error message
102
+ - Shell variables set in pre are visible to the main execution
103
+ - `{{param}}` placeholders are substituted
104
+
105
+ This replaces some uses of guards where richer setup logic is needed. The execution order is:
106
+
107
+ ```text
108
+ guards → pre → main (template exec | passthrough exec | script run)
109
+ ```
110
+
111
+ ### R4: CLI restructuring
112
+
113
+ The CLI is restructured around two top-level commands:
114
+
115
+ #### `nerf validate`
116
+
117
+ Parses and validates one or more manifests. Reports errors. Exits non-zero on any validation
118
+ failure. No output files are written.
119
+
120
+ ```text
121
+ nerf validate [manifest...]
122
+ ```
123
+
124
+ #### `nerf generate`
125
+
126
+ Generates output from manifests for one or more named targets. Each target produces a specific
127
+ output type in a specific directory.
128
+
129
+ ```text
130
+ nerf generate --target <name> [--target <name> ...] [options] [manifest...]
131
+ ```
132
+
133
+ Targets:
134
+
135
+ | Target | What it produces | Current equivalent |
136
+ | --------------- | ---------------------------------------------- | ------------------ |
137
+ | `bin` | Executable shell scripts, one per tool | `nerf build` |
138
+ | `skills` | Rulesync SKILL.md files, one per package | `nerf skill` |
139
+ | `claude-plugin` | Claude Code plugin (skills, scripts, manifest) | `formats.py` |
140
+
141
+ Each target has its own `--outdir` default. Common options (`--prefix`, `--no-default`,
142
+ `--keep-existing`) apply across targets.
143
+
144
+ The `--install-nerfctl` flag is removed from `build` and becomes part of the `claude-plugin`
145
+ target's behavior.
146
+
147
+ ### R5: Manifest versioning
148
+
149
+ Every manifest declares a major version. As we have no need for support of the original design,
150
+ we're starting now with version 1.
151
+
152
+ ```yaml
153
+ version: 1
154
+ ```
155
+
156
+ The version is a single integer at the top level of the manifest file. It determines which fields
157
+ are valid and how the manifest is parsed.
158
+
159
+ **Rules:**
160
+
161
+ - The `version` field is required.
162
+ - The CLI validates against the declared version's schema.
163
+ - The current version is **1**. This is the first versioned manifest format. Prior unversioned
164
+ manifests are not supported — they must be migrated.
165
+ - When the spec changes in backwards-incompatible ways, the version is bumped. Additive changes (new
166
+ optional fields) do not require a version bump.
167
+
168
+ ### R6: Threat model
169
+
170
+ Each tool declares a two-dimensional threat profile: what it **reads** and what it **writes**. This
171
+ enables operators to grant permissions by risk scope rather than enumerating individual tools.
172
+
173
+ ```yaml
174
+ tools:
175
+ git-log:
176
+ threat:
177
+ read: workspace
178
+ write: none
179
+ ...
180
+ git-add:
181
+ threat:
182
+ read: workspace
183
+ write: workspace
184
+ ...
185
+ git-push-branch:
186
+ threat:
187
+ read: workspace
188
+ write: remote
189
+ ...
190
+ ```
191
+
192
+ #### Read vs write
193
+
194
+ The read axis captures what a tool can observe or consume: files in the workspace, the broader
195
+ filesystem, or remote resources. The write axis captures what a tool can modify or affect: nothing,
196
+ the workspace, the local machine, remote systems, or administrative-level operations. By separating
197
+ these two dimensions, we can reason about the risk of a tool more precisely than with a single
198
+ linear scale.
199
+
200
+ **Threat levels (ordered, narrow to broad):**
201
+
202
+ | Level | Meaning |
203
+ | ----------- | ------------------------------------------------------- |
204
+ | `none` | No access in this dimension |
205
+ | `workspace` | Confined to the current workspace directory tree |
206
+ | `machine` | Anywhere on the local filesystem |
207
+ | `remote` | Network operations — APIs, remote repos, cloud services |
208
+ | `admin` | Destructive, irreversible, or elevated operations |
209
+
210
+ The two-axis model captures distinctions that a single axis cannot. For example, `git fetch` (read:
211
+ remote, write: workspace) and `git push` (read: workspace, write: remote) have very different risk
212
+ profiles but would both be "remote" on a single scale. Similarly, `find .` (read: workspace) and
213
+ `find /` (read: machine) are meaningfully different.
214
+
215
+ ### R7: Threat-based grants
216
+
217
+ Operators can grant permissions by threat profile rather than enumerating individual tools. This is
218
+ implemented as a new grant command that works alongside the existing name/pattern-based commands.
219
+
220
+ **Grant-by-threat semantics:**
221
+
222
+ In addition to the existing pattern-based allow and deny grants, we define a way to manage grants by
223
+ threat profile.
224
+
225
+ ```text
226
+ grant-by-threat --read <allowed_threat_level> --write <allowed_threat_level>
227
+ ```
228
+
229
+ A tool is permitted when `tool.read <= grant.read AND tool.write <= grant.write`.
230
+
231
+ Per-tool grants remain available and can be applied on top of (after) threat-based grants. A tool
232
+ can be individually denied even if its threat levels are within the grant ceiling.
233
+
234
+ **Rules:**
235
+
236
+ - The `threat` field is required on every tool, with both `read` and `write`.
237
+ - Allowed threat levels must be one of: `none`, `workspace`, `machine`, `remote`, `admin`.
238
+ - The threat profile is metadata only -- it does not change what the tool does. Enforcement is at
239
+ the permission layer (nerfctl, settings.json, etc.).
240
+
241
+ #### Embedded threat metadata
242
+
243
+ Generated scripts include structured threat metadata in their header comments:
244
+
245
+ ```bash
246
+ #!/usr/bin/env bash
247
+ # nerf-git-push-branch -- Push the current branch to a remote
248
+ # Generated from git manifest. Do not edit directly.
249
+ # nerf:threat:read=workspace
250
+ # nerf:threat:write=remote
251
+ ```
252
+
253
+ This metadata is the source of truth for threat profiles at runtime. It enables tool discovery and
254
+ classification without requiring manifests to be present.
255
+
256
+ The `# nerf:threat:` prefix is a structured tag. Discovery scans for executable files under a root
257
+ directory, greps for these tags, and parses the key=value pairs. This works with any directory
258
+ nesting (flat bin/, nested plugin skills/\*/scripts/, etc.).
259
+
260
+ #### Capabilities
261
+
262
+ The grant system must support two kinds of operations:
263
+
264
+ 1. **Tool discovery** — given a root directory, find all nerf tools and their threat metadata by
265
+ scanning for embedded `# nerf:threat:` tags in generated scripts. This must work across any
266
+ directory nesting (flat bin/, nested plugin skills/\*/scripts/, etc.).
267
+
268
+ 2. **Threat classification** — given a set of tools and a threat ceiling (read + write levels),
269
+ classify each tool as inside or outside the box. A tool is "inside" when
270
+ `tool.read <= ceiling.read AND tool.write <= ceiling.write`. Tools not matching a filter pattern
271
+ are untouched.
272
+
273
+ The tool discovery and classification logic should be framework-agnostic and reusable. The
274
+ framework-specific part is only how results are applied (e.g. writing `Bash(<path>)` entries to
275
+ Claude Code's settings.json).
276
+
277
+ #### Grant commands
278
+
279
+ Five grant commands, each with one clear job. The first three are name/pattern based (unchanged from
280
+ current). The fourth is new and threat-based.
281
+
282
+ **`nerfctl-grant-allow`** `<plugin-root> <pattern> [--settings-scope user|local]`
283
+
284
+ Allow specific tools by name or glob pattern. Unchanged from current behavior.
285
+
286
+ **`nerfctl-grant-deny`** `<plugin-root> <pattern> [--settings-scope user|local]`
287
+
288
+ Deny specific tools by name or glob pattern. Unchanged from current behavior.
289
+
290
+ **`nerfctl-grant-reset`** `<plugin-root> <pattern> [--settings-scope user|local]`
291
+
292
+ Remove specific tools from both allow and deny. Unchanged from current behavior.
293
+
294
+ **`nerfctl-grant-by-threat`** `<plugin-root> --read <level> --write <level>`
295
+ `[--filter <glob>] [--outside deny|reset] [--settings-scope user|local]`
296
+
297
+ Allow all tools within the defined threat box, deny or reset everything outside.
298
+
299
+ ```bash
300
+ # Allow tools with read ≤ workspace AND write ≤ workspace. Deny everything else.
301
+ nerfctl-grant-by-threat <plugin-root> --read workspace --write workspace
302
+
303
+ # Same, but only for git tools. Non-git tools untouched.
304
+ nerfctl-grant-by-threat <plugin-root> --read workspace --write workspace \
305
+ --filter 'nerf-git-*'
306
+
307
+ # Allow up to remote read, workspace write. Reset (don't deny) outside tools.
308
+ nerfctl-grant-by-threat <plugin-root> --read remote --write workspace \
309
+ --outside reset
310
+ ```
311
+
312
+ Parameters:
313
+
314
+ - `--read <level>` (required): read ceiling.
315
+ - `--write <level>` (required): write ceiling.
316
+ - `--filter <glob>` (default: `*`): restricts which tools are affected. Tools not matching the
317
+ filter are untouched.
318
+ - `--outside deny|reset` (default: `deny`): what to do with tools outside the threat box. `deny`
319
+ adds them to the deny list. `reset` removes them from both allow and deny (back to
320
+ ask-every-time).
321
+ - `--settings-scope user|local` (default: `user`): which settings file to modify.
322
+
323
+ **`nerfctl-grant-list`** `[--settings-scope user|local]`
324
+
325
+ List current permissions with threat metadata alongside each tool:
326
+
327
+ ```text
328
+ user (~/.claude/settings.json):
329
+ Allowed:
330
+ nerf-git-log read:workspace write:none
331
+ nerf-git-add read:workspace write:workspace
332
+ nerf-git-commit read:workspace write:workspace
333
+ Denied:
334
+ nerf-git-push-branch read:workspace write:remote
335
+ ```
336
+
337
+ If a tool's script is not found or has no embedded threat metadata, it is listed without
338
+ annotation.
339
+
340
+ #### Operational model
341
+
342
+ All grant commands write to the same flat allow/deny lists in settings.json. There is no layered
343
+ evaluation — **last write wins**. The operator controls ordering.
344
+
345
+ The intended workflow is a convention:
346
+
347
+ ```bash
348
+ # 1. Set baseline with threat levels
349
+ nerfctl-grant-by-threat <root> --read workspace --write workspace
350
+
351
+ # 2. Override specific tools after the baseline
352
+ nerfctl-grant-allow <root> nerf-git-push-branch # allow one that was denied
353
+ nerfctl-grant-deny <root> nerf-git-tag # deny one that was allowed
354
+ ```
355
+
356
+ Re-running step 1 overwrites the overrides from step 2. The operator knows this.
357
+
358
+ To provide visibility, `grant-by-threat` prints what it changes, including when it overrides an
359
+ existing entry:
360
+
361
+ ```text
362
+ Allowed: nerf-git-log read:workspace write:none
363
+ Allowed: nerf-git-add read:workspace write:workspace
364
+ Denied: nerf-git-push-branch read:workspace write:remote (was: allowed)
365
+ ```
366
+
367
+ The `(was: allowed)` annotation gives the operator a chance to notice when a threat-based grant is
368
+ about to clobber an explicit per-tool grant. This is information, not enforcement — the write still
369
+ happens.
370
+
371
+ ### R8: Agent-friendly error messages
372
+
373
+ All runtime errors (validation failures, denied tokens, guard failures) follow a structured format:
374
+
375
+ ```text
376
+ error: <tool-name>: <what went wrong>
377
+ <detail lines>
378
+ hint: <what to do instead>
379
+ ```
380
+
381
+ Errors must be specific enough for an AI agent to parse the failure, understand the constraint, and
382
+ retry correctly without human intervention.
383
+
384
+ ## Future
385
+
386
+ ### Scoped credential injection
387
+
388
+ Per-tool credential injection at provisioning time. This is unchanged from the original nerf tools
389
+ future roadmap and is orthogonal to the manifest changes.
390
+
391
+ ### `deny_regex` for passthrough
392
+
393
+ Regex-based deny patterns for passthrough mode. The `deny_regex` field is reserved in the spec but
394
+ not implemented. Glob patterns (`deny`) cover current needs.
395
+
396
+ ## Out of Scope
397
+
398
+ - **OS-level privilege enforcement** -- unchanged from original design.
399
+ - **Runtime manifest dependency** -- generated scripts remain self-contained.
400
+ - **Passthrough with wrapper parameters** -- passthrough mode does not support explicit
401
+ switches/options/arguments. This avoids complexity in the scan-and-forward model.