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.
- {nerftools-0.3.2 → nerftools-1.0.0}/.cspell.json +2 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/.github/workflows/release-please.yml +8 -9
- {nerftools-0.3.2 → nerftools-1.0.0}/.gitignore +3 -0
- nerftools-1.0.0/.release-please-manifest.json +3 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/CHANGELOG.md +22 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/CONTRIBUTING.md +3 -3
- {nerftools-0.3.2 → nerftools-1.0.0}/PKG-INFO +1 -1
- {nerftools-0.3.2 → nerftools-1.0.0}/README.md +16 -6
- nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/frd.md +401 -0
- nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/hla.md +530 -0
- nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/locked.md +9 -0
- nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/manifest-spec.md +1032 -0
- nerftools-1.0.0/docs/sdd/2026-04-04-nerf-refactor/plan.md +227 -0
- nerftools-1.0.0/nerf.yaml +22 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/__init__.py +0 -1
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/cli.py +49 -18
- nerftools-1.0.0/nerftools/config.py +391 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/formats.py +1 -1
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/.claude-plugin/plugin.json +1 -1
- {nerftools-0.3.2 → nerftools-1.0.0}/pyproject.toml +1 -1
- {nerftools-0.3.2 → nerftools-1.0.0}/release-please-config.json +1 -1
- nerftools-1.0.0/tests/test_config.py +297 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_formats.py +2 -2
- {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_nerfctl.py +1 -15
- {nerftools-0.3.2 → nerftools-1.0.0}/uv.lock +1 -1
- nerftools-0.3.2/.release-please-manifest.json +0 -3
- nerftools-0.3.2/nerf-plugin.yaml +0 -21
- nerftools-0.3.2/nerftools/nerfctl/claude/install-plugin.sh +0 -57
- nerftools-0.3.2/nerftools/plugin_meta.py +0 -200
- nerftools-0.3.2/out/claude-plugin/scripts/nerfctl-install-plugin +0 -57
- nerftools-0.3.2/tests/test_plugin_meta.py +0 -187
- {nerftools-0.3.2 → nerftools-1.0.0}/.claude-plugin/marketplace.json +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/.editorconfig +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/.github/workflows/ci.yml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/.github/workflows/release.yml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/.markdownlint-cli2.jsonc +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/.prettierrc +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/.python-version +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/LICENSE +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/docs/nerf-manifest.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/builder.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/README.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/__init__.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/az-boards.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/az-pipelines.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/az-repos.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/gh.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/git.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/nx.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/pkgrun.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/stdutils.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/tg.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/default_manifests/uv.yaml +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/manifest.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/__init__.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-allow.sh +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-by-threat.sh +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-deny.sh +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-list.sh +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/nerfctl/claude/grant-reset.sh +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/rendering.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/nerftools/skill.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-allow +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-by-threat +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-deny +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-list +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/scripts/nerfctl-grant-reset +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-mywi-comment +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-mywi-show +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-add-parent +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-comment +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-create +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-list +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-show +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-boards/scripts/nerf-az-boards-wi-update +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-pipelines/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-pipelines/scripts/nerf-az-pipelines-check +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-pipelines/scripts/nerf-az-pipelines-list +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-pipelines/scripts/nerf-az-pipelines-runs +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/scripts/nerf-az-repos-pr-comments +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/scripts/nerf-az-repos-pr-create +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/scripts/nerf-az-repos-pr-list +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-az-repos/scripts/nerf-az-repos-pr-show +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-issue-comment +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-issue-create +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-issue-list +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-issue-view +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-comment +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-create +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-diff +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-list +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-review-comments +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-pr-view +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-run-list +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-gh/scripts/nerf-gh-run-view +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-add +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-commit +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-commit-amend +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-fetch +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-log +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-pull +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-push-branch +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-push-main +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-reset-hard-last +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-revert +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-git/scripts/nerf-git-tag +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-affected +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-graph +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-reset +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-run +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-show-project +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-nx/scripts/nerf-nx-show-projects +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-pkgrun/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-pkgrun/scripts/nerf-pkgrun-cspell +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-pkgrun/scripts/nerf-pkgrun-markdownlint +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-pkgrun/scripts/nerf-pkgrun-prettier +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/scripts/nerf-find +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/scripts/nerf-find-cwd +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/scripts/nerf-grep +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-stdutils/scripts/nerf-grep-recursive-cwd +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-fmt +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-fmt-all +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-init +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-init-all +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-output +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-output-all +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-plan +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-plan-all +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-validate +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-tg/scripts/nerf-tg-validate-all +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/scripts/nerf-uv-mypy +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/scripts/nerf-uv-pytest +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/scripts/nerf-uv-ruff-check +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerf-uv/scripts/nerf-uv-ruff-fix +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-allow/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-by-threat/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-deny/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-list/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerfctl-grant-reset/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/out/claude-plugin/skills/nerftools/SKILL.md +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/pypi-dist/.gitignore +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/tests/__init__.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_builder.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_manifest.py +0 -0
- {nerftools-0.3.2 → nerftools-1.0.0}/tests/test_skill.py +0 -0
|
@@ -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@
|
|
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@
|
|
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:
|
|
63
|
-
run: uv sync
|
|
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
|
|
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
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
+
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 (
|
|
104
|
-
uv run nerf generate --target claude-plugin
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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.
|