smartnuf-agent-tools 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. smartnuf_agent_tools-0.1.2/.gitattributes +4 -0
  2. smartnuf_agent_tools-0.1.2/.github/actions/install-native/action.yml +59 -0
  3. smartnuf_agent_tools-0.1.2/.github/pull_request_template.md +25 -0
  4. smartnuf_agent_tools-0.1.2/.github/workflows/ci.yml +171 -0
  5. smartnuf_agent_tools-0.1.2/.github/workflows/native-integration.yml +96 -0
  6. smartnuf_agent_tools-0.1.2/.github/workflows/publish-pypi.yml +83 -0
  7. smartnuf_agent_tools-0.1.2/.github/workflows/release.yml +127 -0
  8. smartnuf_agent_tools-0.1.2/.gitignore +13 -0
  9. smartnuf_agent_tools-0.1.2/AGENTS.md +20 -0
  10. smartnuf_agent_tools-0.1.2/LICENSE +21 -0
  11. smartnuf_agent_tools-0.1.2/PKG-INFO +191 -0
  12. smartnuf_agent_tools-0.1.2/README.md +156 -0
  13. smartnuf_agent_tools-0.1.2/bin/agent-python +9 -0
  14. smartnuf_agent_tools-0.1.2/bin/agent-python.cmd +8 -0
  15. smartnuf_agent_tools-0.1.2/bin/agent-tools +9 -0
  16. smartnuf_agent_tools-0.1.2/bin/agent-tools.cmd +9 -0
  17. smartnuf_agent_tools-0.1.2/docs/decisions/0001-distribution-model.md +36 -0
  18. smartnuf_agent_tools-0.1.2/docs/decisions/0002-native-capability-provider-model.md +147 -0
  19. smartnuf_agent_tools-0.1.2/docs/development-workflow.md +403 -0
  20. smartnuf_agent_tools-0.1.2/docs/packaging.md +52 -0
  21. smartnuf_agent_tools-0.1.2/docs/plan/00-index.md +74 -0
  22. smartnuf_agent_tools-0.1.2/docs/plan/08-releases/01-packaging-discovery.md +60 -0
  23. smartnuf_agent_tools-0.1.2/docs/plan/08-releases/README.md +109 -0
  24. smartnuf_agent_tools-0.1.2/docs/plan/09-capabilities/README.md +52 -0
  25. smartnuf_agent_tools-0.1.2/docs/plan/README.md +69 -0
  26. smartnuf_agent_tools-0.1.2/docs/platforms.md +44 -0
  27. smartnuf_agent_tools-0.1.2/docs/releases/v0.1.0.md +7 -0
  28. smartnuf_agent_tools-0.1.2/docs/releases/v0.1.1.md +37 -0
  29. smartnuf_agent_tools-0.1.2/docs/releases/v0.1.2.md +52 -0
  30. smartnuf_agent_tools-0.1.2/docs/releasing.md +42 -0
  31. smartnuf_agent_tools-0.1.2/pyproject.toml +55 -0
  32. smartnuf_agent_tools-0.1.2/requirements.in +8 -0
  33. smartnuf_agent_tools-0.1.2/requirements.txt +39 -0
  34. smartnuf_agent_tools-0.1.2/scripts/bootstrap.ps1 +75 -0
  35. smartnuf_agent_tools-0.1.2/scripts/bootstrap.sh +40 -0
  36. smartnuf_agent_tools-0.1.2/scripts/install-native.sh +27 -0
  37. smartnuf_agent_tools-0.1.2/scripts/path.ps1 +64 -0
  38. smartnuf_agent_tools-0.1.2/scripts/path.sh +78 -0
  39. smartnuf_agent_tools-0.1.2/scripts/release.py +103 -0
  40. smartnuf_agent_tools-0.1.2/scripts/select_native_integration.py +58 -0
  41. smartnuf_agent_tools-0.1.2/scripts/update.ps1 +10 -0
  42. smartnuf_agent_tools-0.1.2/scripts/update.sh +6 -0
  43. smartnuf_agent_tools-0.1.2/scripts/windows-tools.ps1 +42 -0
  44. smartnuf_agent_tools-0.1.2/src/agent_tools/__init__.py +3 -0
  45. smartnuf_agent_tools-0.1.2/src/agent_tools/__main__.py +3 -0
  46. smartnuf_agent_tools-0.1.2/src/agent_tools/capabilities.py +497 -0
  47. smartnuf_agent_tools-0.1.2/src/agent_tools/cli.py +228 -0
  48. smartnuf_agent_tools-0.1.2/tests/check_distribution.py +138 -0
  49. smartnuf_agent_tools-0.1.2/tests/check_installed_cli.py +99 -0
  50. smartnuf_agent_tools-0.1.2/tests/fixtures/extraneous/pyproject.toml +10 -0
  51. smartnuf_agent_tools-0.1.2/tests/fixtures/extraneous/src/agent_tools_extraneous/__init__.py +1 -0
  52. smartnuf_agent_tools-0.1.2/tests/test_capabilities.py +315 -0
  53. smartnuf_agent_tools-0.1.2/tests/test_cli.py +294 -0
  54. smartnuf_agent_tools-0.1.2/tests/test_install_native.sh +32 -0
  55. smartnuf_agent_tools-0.1.2/tests/test_path.ps1 +69 -0
  56. smartnuf_agent_tools-0.1.2/tests/test_path.sh +84 -0
  57. smartnuf_agent_tools-0.1.2/tests/test_release.py +121 -0
  58. smartnuf_agent_tools-0.1.2/tests/test_select_native_integration.py +25 -0
  59. smartnuf_agent_tools-0.1.2/tests/test_windows_tools.ps1 +36 -0
@@ -0,0 +1,4 @@
1
+ * text=auto
2
+ *.sh text eol=lf
3
+ *.ps1 text eol=crlf
4
+ *.cmd text eol=crlf
@@ -0,0 +1,59 @@
1
+ name: Install native runtime prerequisites
2
+ description: Install and verify Poppler and Ghostscript on supported GitHub runners
3
+
4
+ runs:
5
+ using: composite
6
+ steps:
7
+ - name: Install native tools (Ubuntu)
8
+ if: runner.os == 'Linux'
9
+ shell: bash
10
+ run: |
11
+ echo "[$(date --iso-8601=seconds)] Installing Poppler and Ghostscript with apt."
12
+ sudo apt-get update
13
+ sudo apt-get install -y poppler-utils ghostscript
14
+
15
+ - name: Install native tools (macOS)
16
+ if: runner.os == 'macOS'
17
+ shell: bash
18
+ run: |
19
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Installing Poppler and Ghostscript with Homebrew."
20
+ brew install poppler ghostscript
21
+
22
+ - name: Install native tools (Windows provider path)
23
+ id: windows_native_tools
24
+ if: runner.os == 'Windows'
25
+ shell: pwsh
26
+ run: |
27
+ Write-Host "[$(Get-Date -Format o)] Installing Poppler with WinGet."
28
+ winget install --id oschwartz10612.Poppler -e --accept-package-agreements --accept-source-agreements
29
+ if ($LASTEXITCODE -ne 0) { throw "WinGet Poppler installation failed with exit code $LASTEXITCODE." }
30
+ Write-Host "[$(Get-Date -Format o)] Installing Ghostscript with Chocolatey."
31
+ choco install ghostscript -y --no-progress
32
+ if ($LASTEXITCODE -ne 0) { throw "Chocolatey Ghostscript installation failed with exit code $LASTEXITCODE." }
33
+ $searchRoots = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages", 'C:\Program Files\gs'
34
+ $required = 'pdfinfo.exe', 'pdftotext.exe', 'pdftoppm.exe', 'gswin64c.exe'
35
+ $toolDirectories = foreach ($name in $required) {
36
+ $tool = Get-ChildItem $searchRoots -Filter $name -File -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
37
+ if (-not $tool) { throw "The provider path completed but $name was not found." }
38
+ $tool.DirectoryName
39
+ }
40
+ $machineDirectories = [Environment]::GetEnvironmentVariable('Path', 'Machine') -split ';' | Where-Object { $_ }
41
+ foreach ($directory in @($toolDirectories) + @($machineDirectories) | Select-Object -Unique) {
42
+ $directory | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
43
+ }
44
+
45
+ - name: Report Windows external-provider failure
46
+ if: failure() && runner.os == 'Windows' && steps.windows_native_tools.outcome == 'failure'
47
+ shell: pwsh
48
+ run: |
49
+ Write-Host "[$(Get-Date -Format o)] External Windows native provider path did not complete."
50
+ Get-Process -ErrorAction SilentlyContinue |
51
+ Where-Object { $_.ProcessName -match '^(AppInstallerCLI|choco|gs|msiexec|winget)' } |
52
+ Select-Object ProcessName, Id, StartTime
53
+ $chocolateyLog = 'C:\ProgramData\chocolatey\logs\chocolatey.log'
54
+ if (Test-Path -LiteralPath $chocolateyLog) {
55
+ Write-Host 'Last 200 lines of the Chocolatey log:'
56
+ Get-Content -LiteralPath $chocolateyLog -Tail 200
57
+ } else {
58
+ Write-Warning "Chocolatey log not found at $chocolateyLog."
59
+ }
@@ -0,0 +1,25 @@
1
+ ## Scope
2
+
3
+ - Acceptance criterion or issue:
4
+ - In scope:
5
+ - Non-goals:
6
+ - Base commit:
7
+ - Merge owner:
8
+
9
+ ## Evidence
10
+
11
+ - [ ] Focused commits form one coherent review wave.
12
+ - [ ] Repository-defined local validation passed, including `git diff --check`.
13
+ - [ ] CI and automated review completed for the exact current head.
14
+ - [ ] Findings have evidence-backed dispositions and no actionable thread remains unresolved.
15
+ - [ ] [Adaptation review](https://github.com/smartnuf/agent-tools/blob/main/docs/development-workflow.md#9-review-adaptations) was not triggered, or applicable impact, recovery, reusable lessons, evidence, and durable follow-ups are recorded.
16
+ - [ ] Roadmap/evidence was reconciled when this change advances readiness.
17
+ - [ ] Unrelated dirty state, stashes, branches, worktrees, and user configuration were preserved.
18
+
19
+ ## Integration
20
+
21
+ - Current head:
22
+ - Current `origin/main`:
23
+ - [ ] The base has not moved, or latest `origin/main` was incorporated and the new head was revalidated and rereviewed.
24
+ - [ ] This stream owns merging. If not, stop merge-ready and await ownership.
25
+ - [ ] `--match-head-commit` will guard the verified head and strict required status checks still guard base freshness.
@@ -0,0 +1,171 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ merge_group:
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ package-artifacts:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
22
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
23
+ with:
24
+ version: "0.12.7"
25
+ - name: Validate GitHub workflow syntax and expressions
26
+ shell: bash
27
+ run: |
28
+ archive="$RUNNER_TEMP/actionlint.tar.gz"
29
+ curl --fail --silent --show-error --location \
30
+ --output "$archive" \
31
+ "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz"
32
+ echo "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 $archive" | sha256sum --check --strict
33
+ tar --extract --gzip --file "$archive" --directory "$RUNNER_TEMP" actionlint
34
+ "$RUNNER_TEMP/actionlint"
35
+ - name: Build wheel and source distribution
36
+ run: uv build --out-dir "${{ runner.temp }}/dist"
37
+ - name: Validate artifact metadata and contents
38
+ run: python tests/check_distribution.py "${{ runner.temp }}/dist"
39
+ - name: Validate release version and checksums
40
+ shell: bash
41
+ run: |
42
+ python scripts/release.py verify-tag v0.1.2
43
+ python scripts/release.py checksums "${{ runner.temp }}/dist/SHA256SUMS" "${{ runner.temp }}"/dist/*.whl "${{ runner.temp }}"/dist/*.tar.gz
44
+ - name: Install wheel and declared dependencies
45
+ run: |
46
+ uv venv "${{ runner.temp }}/installed" --python 3.11
47
+ uv pip install --python "${{ runner.temp }}/installed/bin/python" "${{ runner.temp }}"/dist/*.whl
48
+ - name: Test installed CLI and declared Python dependencies without native tools
49
+ run: python tests/check_installed_cli.py "${{ runner.temp }}/installed/bin/agent-tools" "${{ runner.temp }}/dist"
50
+ - name: Verify build and tests leave the checkout unchanged
51
+ run: |
52
+ git diff --exit-code
53
+ test -z "$(git status --porcelain=v1 --untracked-files=all)"
54
+ - name: Upload validated release bundle
55
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
56
+ with:
57
+ name: release-dist
58
+ path: ${{ runner.temp }}/dist/
59
+ if-no-files-found: error
60
+ retention-days: 1
61
+
62
+ install-and-test:
63
+ needs: package-artifacts
64
+ strategy:
65
+ fail-fast: false
66
+ matrix:
67
+ os: [windows-latest, ubuntu-latest, macos-latest]
68
+ runs-on: ${{ matrix.os }}
69
+ steps:
70
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
71
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
72
+ with:
73
+ version: "0.12.7"
74
+ - name: Download shared release bundle
75
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
76
+ with:
77
+ name: release-dist
78
+ path: ${{ runner.temp }}/release-dist
79
+
80
+ - name: Install and test shared release wheel (Windows)
81
+ if: runner.os == 'Windows'
82
+ shell: pwsh
83
+ env:
84
+ UV_TOOL_DIR: ${{ runner.temp }}\release-tools
85
+ UV_TOOL_BIN_DIR: ${{ runner.temp }}\release-bin
86
+ run: |
87
+ python scripts/release.py verify-checksums "$env:RUNNER_TEMP\release-dist\SHA256SUMS"
88
+ $wheel = Get-ChildItem "$env:RUNNER_TEMP\release-dist" -Filter '*.whl' | Select-Object -ExpandProperty FullName -First 1
89
+ uv tool install --python 3.13 $wheel
90
+ python tests/check_installed_cli.py "$(uv tool dir --bin)\agent-tools.exe" "$env:RUNNER_TEMP\release-dist"
91
+
92
+ - name: Install and test shared release wheel (Unix)
93
+ if: runner.os != 'Windows'
94
+ shell: bash
95
+ env:
96
+ UV_TOOL_DIR: ${{ runner.temp }}/release-tools
97
+ UV_TOOL_BIN_DIR: ${{ runner.temp }}/release-bin
98
+ run: |
99
+ python scripts/release.py verify-checksums "$RUNNER_TEMP/release-dist/SHA256SUMS"
100
+ uv tool install --python 3.13 "$RUNNER_TEMP"/release-dist/*.whl
101
+ python tests/check_installed_cli.py "$(uv tool dir --bin)/agent-tools" "$RUNNER_TEMP/release-dist"
102
+
103
+ - name: Bootstrap twice (Windows)
104
+ if: runner.os == 'Windows'
105
+ shell: pwsh
106
+ run: |
107
+ ./scripts/bootstrap.ps1
108
+ uv pip install --python .venv -e tests/fixtures/extraneous
109
+ ./scripts/bootstrap.ps1
110
+ .venv\Scripts\python.exe -c "import importlib.util; assert importlib.util.find_spec('agent_tools_extraneous') is None"
111
+ ./scripts/path.ps1
112
+
113
+ - name: Bootstrap twice (Unix)
114
+ if: runner.os != 'Windows'
115
+ shell: bash
116
+ run: |
117
+ sh scripts/bootstrap.sh
118
+ uv pip install --python .venv -e tests/fixtures/extraneous
119
+ sh scripts/bootstrap.sh
120
+ .venv/bin/python -c "import importlib.util; assert importlib.util.find_spec('agent_tools_extraneous') is None"
121
+ sh scripts/path.sh
122
+
123
+ - name: Exercise checkout doctor (Windows)
124
+ if: runner.os == 'Windows'
125
+ shell: pwsh
126
+ run: |
127
+ $output = & .\bin\agent-tools.cmd doctor 2>&1 | Out-String
128
+ $exitCode = $LASTEXITCODE
129
+ $output
130
+ if ($exitCode -notin 0, 1) { throw "checkout doctor failed with exit code $exitCode" }
131
+ if ($output -match 'Traceback') { throw 'checkout doctor emitted a traceback' }
132
+ exit 0
133
+
134
+ - name: Exercise checkout doctor (Unix)
135
+ if: runner.os != 'Windows'
136
+ shell: bash
137
+ run: |
138
+ set +e
139
+ output=$(./bin/agent-tools doctor 2>&1)
140
+ status=$?
141
+ set -e
142
+ printf '%s\n' "$output"
143
+ test "$status" -eq 0 || test "$status" -eq 1
144
+ case "$output" in *Traceback*) exit 1;; esac
145
+
146
+ - name: Run unit tests (Windows)
147
+ if: runner.os == 'Windows'
148
+ shell: pwsh
149
+ run: .venv\Scripts\python.exe -m unittest discover -s tests
150
+
151
+ - name: Test PATH backup and restoration (Windows)
152
+ if: runner.os == 'Windows'
153
+ shell: pwsh
154
+ run: ./tests/test_path.ps1
155
+
156
+ - name: Test Windows native-tool discovery
157
+ if: runner.os == 'Windows'
158
+ shell: pwsh
159
+ run: ./tests/test_windows_tools.ps1
160
+
161
+ - name: Run unit tests (Unix)
162
+ if: runner.os != 'Windows'
163
+ run: .venv/bin/python -m unittest discover -s tests
164
+
165
+ - name: Test PATH profile handling (Unix)
166
+ if: runner.os != 'Windows'
167
+ run: sh tests/test_path.sh
168
+
169
+ - name: Test privileged native installation routing (Unix)
170
+ if: runner.os != 'Windows'
171
+ run: sh tests/test_install_native.sh
@@ -0,0 +1,96 @@
1
+ name: Native integration
2
+
3
+ on:
4
+ pull_request:
5
+ schedule:
6
+ - cron: "23 5 * * 1"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ select-native-integration:
18
+ runs-on: ubuntu-latest
19
+ outputs:
20
+ run-native: ${{ steps.select.outputs.run-native }}
21
+ steps:
22
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
23
+ with:
24
+ fetch-depth: 0
25
+ - name: Select native integration
26
+ id: select
27
+ shell: bash
28
+ env:
29
+ EVENT_NAME: ${{ github.event_name }}
30
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
31
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
32
+ run: |
33
+ if test "$EVENT_NAME" = pull_request; then
34
+ selected="$(python scripts/select_native_integration.py --base "$BASE_SHA" --head "$HEAD_SHA")"
35
+ else
36
+ selected=true
37
+ fi
38
+ echo "run-native=$selected" | tee -a "$GITHUB_OUTPUT"
39
+ echo "Native integration selected: $selected"
40
+
41
+ build-native-test-artifact:
42
+ needs: select-native-integration
43
+ if: needs.select-native-integration.outputs.run-native == 'true'
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
47
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
48
+ with:
49
+ version: "0.12.7"
50
+ - run: uv build --out-dir "${{ runner.temp }}/dist"
51
+ - run: python tests/check_distribution.py "${{ runner.temp }}/dist"
52
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
53
+ with:
54
+ name: native-test-dist
55
+ path: ${{ runner.temp }}/dist/
56
+ if-no-files-found: error
57
+ retention-days: 1
58
+
59
+ native-install-and-test:
60
+ needs: build-native-test-artifact
61
+ strategy:
62
+ fail-fast: false
63
+ matrix:
64
+ os: [windows-latest, ubuntu-latest, macos-latest]
65
+ runs-on: ${{ matrix.os }}
66
+ steps:
67
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
68
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
69
+ with:
70
+ version: "0.12.7"
71
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
72
+ with:
73
+ name: native-test-dist
74
+ path: ${{ runner.temp }}/native-test-dist
75
+ - name: Install native tools through external providers
76
+ timeout-minutes: 10
77
+ uses: ./.github/actions/install-native
78
+ - name: Install and test wheel (Windows)
79
+ if: runner.os == 'Windows'
80
+ shell: pwsh
81
+ env:
82
+ UV_TOOL_DIR: ${{ runner.temp }}\native-tools
83
+ UV_TOOL_BIN_DIR: ${{ runner.temp }}\native-bin
84
+ run: |
85
+ $wheel = Get-ChildItem "$env:RUNNER_TEMP\native-test-dist" -Filter '*.whl' | Select-Object -ExpandProperty FullName -First 1
86
+ uv tool install --python 3.13 $wheel
87
+ python tests/check_installed_cli.py "$(uv tool dir --bin)\agent-tools.exe" "$env:RUNNER_TEMP\native-test-dist" --native-tools require
88
+ - name: Install and test wheel (Unix)
89
+ if: runner.os != 'Windows'
90
+ shell: bash
91
+ env:
92
+ UV_TOOL_DIR: ${{ runner.temp }}/native-tools
93
+ UV_TOOL_BIN_DIR: ${{ runner.temp }}/native-bin
94
+ run: |
95
+ uv tool install --python 3.13 "$RUNNER_TEMP"/native-test-dist/*.whl
96
+ python tests/check_installed_cli.py "$(uv tool dir --bin)/agent-tools" "$RUNNER_TEMP/native-test-dist" --native-tools require
@@ -0,0 +1,83 @@
1
+ name: Publish stable release to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published, released]
6
+ workflow_dispatch:
7
+ inputs:
8
+ release_tag:
9
+ description: Existing stable GitHub release tag to publish or recover
10
+ required: true
11
+ type: string
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ publish:
18
+ if: github.event_name == 'workflow_dispatch' || (github.event.release.prerelease == false && github.event.release.draft == false)
19
+ runs-on: ubuntu-latest
20
+ environment:
21
+ name: pypi
22
+ url: https://pypi.org/p/smartnuf-agent-tools
23
+ permissions:
24
+ contents: read
25
+ id-token: write
26
+ env:
27
+ RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }}
28
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
29
+ steps:
30
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
31
+ with:
32
+ fetch-depth: 0
33
+ - name: Verify reviewed stable release tag
34
+ env:
35
+ GH_TOKEN: ${{ github.token }}
36
+ shell: bash
37
+ run: |
38
+ [[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
39
+ test "$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" --jq '.draft or .prerelease')" = false
40
+ git fetch --force origin "refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG"
41
+ git fetch --no-tags origin "$DEFAULT_BRANCH:refs/remotes/origin/$DEFAULT_BRANCH"
42
+ tag_commit="$(git rev-list -n 1 "$RELEASE_TAG")"
43
+ git merge-base --is-ancestor "$tag_commit" "origin/$DEFAULT_BRANCH"
44
+ tag_version="$(git show "$RELEASE_TAG:src/agent_tools/__init__.py" | sed -n 's/^__version__ = "\([^"]*\)"$/\1/p')"
45
+ test "$tag_version" = "${RELEASE_TAG#v}"
46
+ - name: Download and verify GitHub release artifacts
47
+ env:
48
+ GH_TOKEN: ${{ github.token }}
49
+ shell: bash
50
+ run: |
51
+ mkdir dist publish
52
+ gh release download "$RELEASE_TAG" \
53
+ --pattern '*.whl' \
54
+ --pattern '*.tar.gz' \
55
+ --pattern SHA256SUMS \
56
+ --dir dist
57
+ version="${RELEASE_TAG#v}"
58
+ tag_commit="$(git rev-list -n 1 "$RELEASE_TAG")"
59
+ wheel="smartnuf_agent_tools-$version-py3-none-any.whl"
60
+ sdist="smartnuf_agent_tools-$version.tar.gz"
61
+ test -f "dist/$wheel"
62
+ test -f "dist/$sdist"
63
+ printf '%s\n' "$wheel" "$sdist" | sort > expected-files
64
+ awk 'NF {print $2}' dist/SHA256SUMS | sort > manifest-files
65
+ diff -u expected-files manifest-files
66
+ (cd dist && sha256sum --check SHA256SUMS)
67
+ gh attestation verify "dist/$wheel" \
68
+ --repo "$GITHUB_REPOSITORY" \
69
+ --signer-workflow "$GITHUB_REPOSITORY/.github/workflows/release.yml" \
70
+ --source-ref "refs/tags/$RELEASE_TAG" \
71
+ --source-digest "$tag_commit"
72
+ gh attestation verify "dist/$sdist" \
73
+ --repo "$GITHUB_REPOSITORY" \
74
+ --signer-workflow "$GITHUB_REPOSITORY/.github/workflows/release.yml" \
75
+ --source-ref "refs/tags/$RELEASE_TAG" \
76
+ --source-digest "$tag_commit"
77
+ cp dist/*.whl dist/*.tar.gz publish/
78
+ test "$(find publish -maxdepth 1 -type f -name '*.whl' | wc -l)" -eq 1
79
+ test "$(find publish -maxdepth 1 -type f -name '*.tar.gz' | wc -l)" -eq 1
80
+ - name: Publish distributions with GitHub OIDC
81
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33
82
+ with:
83
+ packages-dir: publish
@@ -0,0 +1,127 @@
1
+ name: GitHub prerelease
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v[0-9]*"
7
+ workflow_dispatch:
8
+ inputs:
9
+ release_tag:
10
+ description: Existing GitHub release tag to smoke-test
11
+ required: true
12
+ type: string
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ release:
19
+ if: github.event_name == 'push'
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ attestations: write
23
+ contents: write
24
+ id-token: write
25
+ env:
26
+ RELEASE_TAG: ${{ github.ref_name }}
27
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
28
+ steps:
29
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
30
+ with:
31
+ fetch-depth: 0
32
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
33
+ with:
34
+ version: "0.12.7"
35
+ - name: Verify reviewed tag and package version
36
+ shell: bash
37
+ run: |
38
+ python scripts/release.py verify-tag "$RELEASE_TAG"
39
+ test -f "docs/releases/$RELEASE_TAG.md"
40
+ git fetch --no-tags origin "$DEFAULT_BRANCH:refs/remotes/origin/$DEFAULT_BRANCH"
41
+ tag_commit="$(git rev-list -n 1 "$RELEASE_TAG")"
42
+ git merge-base --is-ancestor "$tag_commit" "origin/$DEFAULT_BRANCH"
43
+ - name: Build and validate artifacts
44
+ shell: bash
45
+ run: |
46
+ uv build --out-dir dist
47
+ python tests/check_distribution.py dist
48
+ python scripts/release.py checksums dist/SHA256SUMS dist/*.whl dist/*.tar.gz
49
+ - name: Attest distributions built from the release tag
50
+ uses: actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1
51
+ with:
52
+ subject-path: |
53
+ dist/*.whl
54
+ dist/*.tar.gz
55
+ - name: Create GitHub prerelease
56
+ env:
57
+ GH_TOKEN: ${{ github.token }}
58
+ shell: bash
59
+ run: |
60
+ gh release create "$RELEASE_TAG" \
61
+ dist/*.whl dist/*.tar.gz dist/SHA256SUMS \
62
+ --verify-tag \
63
+ --prerelease \
64
+ --notes-file "docs/releases/$RELEASE_TAG.md" \
65
+ --title "agent-tools $RELEASE_TAG"
66
+
67
+ smoke-release:
68
+ needs: release
69
+ if: always() && (github.event_name == 'workflow_dispatch' || needs.release.result == 'success')
70
+ strategy:
71
+ fail-fast: false
72
+ matrix:
73
+ os: [windows-latest, ubuntu-latest, macos-latest]
74
+ runs-on: ${{ matrix.os }}
75
+ permissions:
76
+ contents: read
77
+ env:
78
+ RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }}
79
+ steps:
80
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
81
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
82
+ with:
83
+ version: "0.12.7"
84
+ - name: Qualify fresh native installation through external providers
85
+ timeout-minutes: 10
86
+ uses: ./.github/actions/install-native
87
+ - name: Install, pin, and uninstall published wheel (Windows)
88
+ if: runner.os == 'Windows'
89
+ shell: pwsh
90
+ env:
91
+ UV_TOOL_DIR: ${{ runner.temp }}\release-tools
92
+ UV_TOOL_BIN_DIR: ${{ runner.temp }}\release-bin
93
+ run: |
94
+ $version = $env:RELEASE_TAG.TrimStart('v')
95
+ $wheel = "smartnuf_agent_tools-$version-py3-none-any.whl"
96
+ $url = "https://github.com/$env:GITHUB_REPOSITORY/releases/download/$env:RELEASE_TAG/$wheel"
97
+ uv tool install --python 3.13 $url
98
+ if ($LASTEXITCODE -ne 0) { throw "initial tool install failed with exit code $LASTEXITCODE" }
99
+ $executable = "$(uv tool dir --bin)\agent-tools.exe"
100
+ if ($LASTEXITCODE -ne 0) { throw "tool bin lookup failed with exit code $LASTEXITCODE" }
101
+ $reportedVersion = & $executable --version
102
+ if ($LASTEXITCODE -ne 0) { throw "version command failed with exit code $LASTEXITCODE" }
103
+ if ($reportedVersion -ne "agent-tools $version") { throw "unexpected version output: $reportedVersion" }
104
+ & $executable doctor
105
+ if ($LASTEXITCODE -ne 0) { throw "complete native doctor failed with exit code $LASTEXITCODE" }
106
+ uv tool install --python 3.13 --reinstall $url
107
+ if ($LASTEXITCODE -ne 0) { throw "pinned tool reinstall failed with exit code $LASTEXITCODE" }
108
+ uv tool uninstall smartnuf-agent-tools
109
+ if ($LASTEXITCODE -ne 0) { throw "tool uninstall failed with exit code $LASTEXITCODE" }
110
+ if (Test-Path $executable) { throw 'agent-tools executable remains after uninstall' }
111
+ - name: Install, pin, and uninstall published wheel (Unix)
112
+ if: runner.os != 'Windows'
113
+ shell: bash
114
+ env:
115
+ UV_TOOL_DIR: ${{ runner.temp }}/release-tools
116
+ UV_TOOL_BIN_DIR: ${{ runner.temp }}/release-bin
117
+ run: |
118
+ version="${RELEASE_TAG#v}"
119
+ wheel="smartnuf_agent_tools-$version-py3-none-any.whl"
120
+ url="https://github.com/$GITHUB_REPOSITORY/releases/download/$RELEASE_TAG/$wheel"
121
+ uv tool install --python 3.13 "$url"
122
+ tool_bin="$(uv tool dir --bin)"
123
+ test "$("$tool_bin/agent-tools" --version)" = "agent-tools $version"
124
+ "$tool_bin/agent-tools" doctor
125
+ uv tool install --python 3.13 --reinstall "$url"
126
+ uv tool uninstall smartnuf-agent-tools
127
+ test ! -e "$tool_bin/agent-tools"
@@ -0,0 +1,13 @@
1
+ .venv/
2
+ .tools/
3
+ .cache/
4
+ .backups/
5
+ tmp/
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+ __pycache__/
10
+ *.py[cod]
11
+ *.log
12
+ .DS_Store
13
+ Thumbs.db
@@ -0,0 +1,20 @@
1
+ # Repository instructions
2
+
3
+ This repository contains reusable, user-level tools for coding agents and humans.
4
+
5
+ - Keep scripts cross-platform, idempotent, and safe to rerun.
6
+ - Never commit credentials, tokens, private source material, `.venv/`, caches, or downloaded binaries.
7
+ - Prefer official platform package managers for native programs and `uv` for Python environments.
8
+ - Do not silently alter a user's shell profile, user `PATH`, or system packages. Require an explicit flag and report every change.
9
+ - Pin or constrain dependencies deliberately; review upgrades before changing them.
10
+ - Put portable Python code in `src/agent_tools/`, launchers in `bin/`, platform automation in `scripts/`, and configuration in `config/`.
11
+ - Preserve compatibility with Windows PowerShell 7+, Linux, and macOS. Where behavior differs, document and test the difference.
12
+ - Downloads outside package managers must use official HTTPS sources and verify a published checksum or signature.
13
+ - Prefer additive, reviewable changes. Do not overwrite user configuration without a backup and explicit authorization.
14
+ - Run `python -m unittest discover -s tests`, the platform script syntax checks, and `agent-tools doctor` when relevant.
15
+ - Bound Windows CI steps that invoke native-tool installers with explicit timeouts. After inspecting a failure, retry the same head at most once; never push an empty or unrelated commit merely to retrigger CI.
16
+ - Read `docs/plan/00-index.md` before planning roadmap work. Keep its status, effort forecast, and evidence links current in the same change that advances a milestone.
17
+ - For non-trivial work, identify the milestone and acceptance criterion, write a task plan with estimated effort before implementation, and report completed work, validation, changed estimates, and the recommended next task afterward.
18
+ - Mark roadmap criteria complete only with durable evidence such as a merged change, passing test, published artifact, or release. Do not infer completion from prose or issue counts.
19
+ - Follow [`docs/development-workflow.md`](docs/development-workflow.md) for branch, pull-request, review, and merge work. Merge evidence must apply to the exact current head, concurrent autonomous streams must serialize integration through one merge owner, and pre-existing repository or user state must be preserved.
20
+ - Respect any more-specific `AGENTS.md` in subdirectories.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 smartnuf contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.