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.
- smartnuf_agent_tools-0.1.2/.gitattributes +4 -0
- smartnuf_agent_tools-0.1.2/.github/actions/install-native/action.yml +59 -0
- smartnuf_agent_tools-0.1.2/.github/pull_request_template.md +25 -0
- smartnuf_agent_tools-0.1.2/.github/workflows/ci.yml +171 -0
- smartnuf_agent_tools-0.1.2/.github/workflows/native-integration.yml +96 -0
- smartnuf_agent_tools-0.1.2/.github/workflows/publish-pypi.yml +83 -0
- smartnuf_agent_tools-0.1.2/.github/workflows/release.yml +127 -0
- smartnuf_agent_tools-0.1.2/.gitignore +13 -0
- smartnuf_agent_tools-0.1.2/AGENTS.md +20 -0
- smartnuf_agent_tools-0.1.2/LICENSE +21 -0
- smartnuf_agent_tools-0.1.2/PKG-INFO +191 -0
- smartnuf_agent_tools-0.1.2/README.md +156 -0
- smartnuf_agent_tools-0.1.2/bin/agent-python +9 -0
- smartnuf_agent_tools-0.1.2/bin/agent-python.cmd +8 -0
- smartnuf_agent_tools-0.1.2/bin/agent-tools +9 -0
- smartnuf_agent_tools-0.1.2/bin/agent-tools.cmd +9 -0
- smartnuf_agent_tools-0.1.2/docs/decisions/0001-distribution-model.md +36 -0
- smartnuf_agent_tools-0.1.2/docs/decisions/0002-native-capability-provider-model.md +147 -0
- smartnuf_agent_tools-0.1.2/docs/development-workflow.md +403 -0
- smartnuf_agent_tools-0.1.2/docs/packaging.md +52 -0
- smartnuf_agent_tools-0.1.2/docs/plan/00-index.md +74 -0
- smartnuf_agent_tools-0.1.2/docs/plan/08-releases/01-packaging-discovery.md +60 -0
- smartnuf_agent_tools-0.1.2/docs/plan/08-releases/README.md +109 -0
- smartnuf_agent_tools-0.1.2/docs/plan/09-capabilities/README.md +52 -0
- smartnuf_agent_tools-0.1.2/docs/plan/README.md +69 -0
- smartnuf_agent_tools-0.1.2/docs/platforms.md +44 -0
- smartnuf_agent_tools-0.1.2/docs/releases/v0.1.0.md +7 -0
- smartnuf_agent_tools-0.1.2/docs/releases/v0.1.1.md +37 -0
- smartnuf_agent_tools-0.1.2/docs/releases/v0.1.2.md +52 -0
- smartnuf_agent_tools-0.1.2/docs/releasing.md +42 -0
- smartnuf_agent_tools-0.1.2/pyproject.toml +55 -0
- smartnuf_agent_tools-0.1.2/requirements.in +8 -0
- smartnuf_agent_tools-0.1.2/requirements.txt +39 -0
- smartnuf_agent_tools-0.1.2/scripts/bootstrap.ps1 +75 -0
- smartnuf_agent_tools-0.1.2/scripts/bootstrap.sh +40 -0
- smartnuf_agent_tools-0.1.2/scripts/install-native.sh +27 -0
- smartnuf_agent_tools-0.1.2/scripts/path.ps1 +64 -0
- smartnuf_agent_tools-0.1.2/scripts/path.sh +78 -0
- smartnuf_agent_tools-0.1.2/scripts/release.py +103 -0
- smartnuf_agent_tools-0.1.2/scripts/select_native_integration.py +58 -0
- smartnuf_agent_tools-0.1.2/scripts/update.ps1 +10 -0
- smartnuf_agent_tools-0.1.2/scripts/update.sh +6 -0
- smartnuf_agent_tools-0.1.2/scripts/windows-tools.ps1 +42 -0
- smartnuf_agent_tools-0.1.2/src/agent_tools/__init__.py +3 -0
- smartnuf_agent_tools-0.1.2/src/agent_tools/__main__.py +3 -0
- smartnuf_agent_tools-0.1.2/src/agent_tools/capabilities.py +497 -0
- smartnuf_agent_tools-0.1.2/src/agent_tools/cli.py +228 -0
- smartnuf_agent_tools-0.1.2/tests/check_distribution.py +138 -0
- smartnuf_agent_tools-0.1.2/tests/check_installed_cli.py +99 -0
- smartnuf_agent_tools-0.1.2/tests/fixtures/extraneous/pyproject.toml +10 -0
- smartnuf_agent_tools-0.1.2/tests/fixtures/extraneous/src/agent_tools_extraneous/__init__.py +1 -0
- smartnuf_agent_tools-0.1.2/tests/test_capabilities.py +315 -0
- smartnuf_agent_tools-0.1.2/tests/test_cli.py +294 -0
- smartnuf_agent_tools-0.1.2/tests/test_install_native.sh +32 -0
- smartnuf_agent_tools-0.1.2/tests/test_path.ps1 +69 -0
- smartnuf_agent_tools-0.1.2/tests/test_path.sh +84 -0
- smartnuf_agent_tools-0.1.2/tests/test_release.py +121 -0
- smartnuf_agent_tools-0.1.2/tests/test_select_native_integration.py +25 -0
- smartnuf_agent_tools-0.1.2/tests/test_windows_tools.ps1 +36 -0
|
@@ -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,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.
|