Merge pull request #35 from bnema/rename/zut
feat!: rename zot to zut
322 files · +2559 -2384
M .github/ISSUE_TEMPLATE/bug_report.yml +2 -2
| @@ -24,7 +24,7 @@ body: | |||
| 24 | 24 | attributes: | |
| 25 | 25 | label: Expected behavior | |
| 26 | 26 | description: What should have happened instead? | |
| 27 | - | placeholder: "I expected zot to..." | |
| 27 | + | placeholder: "I expected zut to..." | |
| 28 | 28 | validations: | |
| 29 | 29 | required: true | |
| 30 | 30 | ||
| @@ -32,7 +32,7 @@ body: | |||
| 32 | 32 | id: version | |
| 33 | 33 | attributes: | |
| 34 | 34 | label: Version | |
| 35 | - | description: "e.g. 0.0.1 — run `zot --version` to check" | |
| 35 | + | description: "e.g. 0.0.1 — run `zut --version` to check" | |
| 36 | 36 | validations: | |
| 37 | 37 | required: true | |
| 38 | 38 | ||
M .github/ISSUE_TEMPLATE/feature_request.yml +2 -2
| @@ -8,7 +8,7 @@ body: | |||
| 8 | 8 | value: | | |
| 9 | 9 | Thanks for the idea! A short conversation here saves everyone time before any code is written. | |
| 10 | 10 | ||
| 11 | - | Zot is intentionally focused — not every idea will make it in. A "no" is not a judgment of the idea. | |
| 11 | + | Zut is intentionally focused — not every idea will make it in. A "no" is not a judgment of the idea. | |
| 12 | 12 | ||
| 13 | 13 | - type: textarea | |
| 14 | 14 | id: problem | |
| @@ -16,7 +16,7 @@ body: | |||
| 16 | 16 | label: What problem does this solve? | |
| 17 | 17 | description: | | |
| 18 | 18 | The **why**, not the **what**. What were you trying to do when you wished this existed? | |
| 19 | - | placeholder: "When I'm working with multiple providers, I want to switch between them without restarting zot." | |
| 19 | + | placeholder: "When I'm working with multiple providers, I want to switch between them without restarting zut." | |
| 20 | 20 | validations: | |
| 21 | 21 | required: true | |
| 22 | 22 | ||
A .github/scripts/next-release-tag.sh +60 -0
| @@ -0,0 +1,60 @@ | |||
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | # Print the release tag for HEAD, or nothing when its unreleased commits do not | |
| 3 | + | # require a release. A tag already pointing to HEAD is emitted again so a | |
| 4 | + | # failed GoReleaser job can be retried without minting a second tag. | |
| 5 | + | set -euo pipefail | |
| 6 | + | ||
| 7 | + | latest=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n1) | |
| 8 | + | head=$(git rev-parse HEAD) | |
| 9 | + | if [ -n "$latest" ] && [ "$(git rev-parse "$latest^{}")" = "$head" ]; then | |
| 10 | + | printf '%s\n' "$latest" | |
| 11 | + | exit 0 | |
| 12 | + | fi | |
| 13 | + | ||
| 14 | + | if [ -n "$latest" ]; then | |
| 15 | + | range="${latest}..HEAD" | |
| 16 | + | else | |
| 17 | + | range="HEAD" | |
| 18 | + | fi | |
| 19 | + | ||
| 20 | + | minor=false | |
| 21 | + | patch=false | |
| 22 | + | while IFS= read -r commit; do | |
| 23 | + | subject=$(git log -1 --format=%s "$commit") | |
| 24 | + | body=$(git log -1 --format=%b "$commit") | |
| 25 | + | ||
| 26 | + | # A breaking change is a v0.x minor release. | |
| 27 | + | if grep -Eq '^[[:alpha:]]+(\([^)]+\))?!:' <<<"$subject" || grep -Eiq '^BREAKING[ -]CHANGE:' <<<"$body"; then | |
| 28 | + | minor=true | |
| 29 | + | continue | |
| 30 | + | fi | |
| 31 | + | ||
| 32 | + | type=${subject%%(*} | |
| 33 | + | type=${type%%:*} | |
| 34 | + | type=${type%%!*} | |
| 35 | + | type=$(tr '[:upper:]' '[:lower:]' <<<"$type") | |
| 36 | + | case "$type" in | |
| 37 | + | feat) minor=true ;; | |
| 38 | + | fix|perf|refactor|build|chore|ci) patch=true ;; | |
| 39 | + | docs|test|style) ;; | |
| 40 | + | *) ;; | |
| 41 | + | esac | |
| 42 | + | done < <(git rev-list "$range") | |
| 43 | + | ||
| 44 | + | if [ "$minor" != true ] && [ "$patch" != true ]; then | |
| 45 | + | exit 0 | |
| 46 | + | fi | |
| 47 | + | ||
| 48 | + | # The renamed project starts a new public release line at v0.1.0. | |
| 49 | + | if [ -z "$latest" ]; then | |
| 50 | + | printf '%s\n' 'v0.1.0' | |
| 51 | + | exit 0 | |
| 52 | + | fi | |
| 53 | + | ||
| 54 | + | version=${latest#v} | |
| 55 | + | IFS=. read -r major minor_version patch_version <<<"$version" | |
| 56 | + | if [ "$minor" = true ]; then | |
| 57 | + | printf 'v%s.%s.0\n' "$major" "$((minor_version + 1))" | |
| 58 | + | else | |
| 59 | + | printf 'v%s.%s.%s\n' "$major" "$minor_version" "$((patch_version + 1))" | |
| 60 | + | fi | |
A .github/scripts/next-release-tag_test.sh +39 -0
| @@ -0,0 +1,39 @@ | |||
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | set -euo pipefail | |
| 3 | + | ||
| 4 | + | repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) | |
| 5 | + | script="$repo_root/.github/scripts/next-release-tag.sh" | |
| 6 | + | tmp=$(mktemp -d) | |
| 7 | + | trap 'rm -rf "$tmp"' EXIT | |
| 8 | + | ||
| 9 | + | cd "$tmp" | |
| 10 | + | git init --quiet | |
| 11 | + | git config user.name 'Release Test' | |
| 12 | + | git config user.email 'release-test@example.invalid' | |
| 13 | + | ||
| 14 | + | git commit --allow-empty --quiet -m 'feat: first release' | |
| 15 | + | expect() { | |
| 16 | + | local want=$1 | |
| 17 | + | local got | |
| 18 | + | got=$($script) | |
| 19 | + | if [ "$got" != "$want" ]; then | |
| 20 | + | printf 'next-release-tag = %q, want %q\n' "$got" "$want" >&2 | |
| 21 | + | exit 1 | |
| 22 | + | fi | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | expect 'v0.1.0' | |
| 26 | + | git tag -a v0.1.0 -m 'release v0.1.0' | |
| 27 | + | ||
| 28 | + | # A rerun after tag creation must release the existing tag, not skip it. | |
| 29 | + | expect 'v0.1.0' | |
| 30 | + | ||
| 31 | + | git commit --allow-empty --quiet -m 'docs: explain releases' | |
| 32 | + | expect '' | |
| 33 | + | ||
| 34 | + | git commit --allow-empty --quiet -m 'fix: repair installer' | |
| 35 | + | expect 'v0.1.1' | |
| 36 | + | git tag -a v0.1.1 -m 'release v0.1.1' | |
| 37 | + | ||
| 38 | + | git commit --allow-empty --quiet -m 'refactor!: replace extension handshake' | |
| 39 | + | expect 'v0.2.0' | |
M .github/workflows/ci.yml +3 -0
| @@ -53,6 +53,9 @@ jobs: | |||
| 53 | 53 | steps: | |
| 54 | 54 | - uses: actions/checkout@v4 | |
| 55 | 55 | ||
| 56 | + | - name: release-version policy | |
| 57 | + | run: bash .github/scripts/next-release-tag_test.sh | |
| 58 | + | ||
| 56 | 59 | - name: set up go | |
| 57 | 60 | uses: actions/setup-go@v7 | |
| 58 | 61 | with: | |
M .github/workflows/release.yml +36 -71
| @@ -1,18 +1,8 @@ | |||
| 1 | 1 | name: release | |
| 2 | 2 | ||
| 3 | - | # Every push to main that passes CI cuts a new release. The workflow: | |
| 4 | - | # 1. waits for the ci workflow on the same commit to succeed | |
| 5 | - | # 2. looks up the latest existing vX.Y.Z tag | |
| 6 | - | # 3. bumps the patch number (vX.Y.Z+1) — or starts at v0.0.1 if none | |
| 7 | - | # 4. creates and pushes that tag | |
| 8 | - | # 5. runs goreleaser, which builds binaries for linux/darwin/windows | |
| 9 | - | # (amd64 + arm64) and attaches them plus checksums.txt to a fresh | |
| 10 | - | # GitHub Release page | |
| 11 | - | # | |
| 12 | - | # Skip a release by putting [release=skip] anywhere in the commit | |
| 13 | - | # message — useful for docs-only or ci-only commits. We use a rare | |
| 14 | - | # equals-sign form so commits that merely *mention* the feature | |
| 15 | - | # ("make the release step optional") don't accidentally opt out. | |
| 3 | + | # After CI succeeds on main, create at most one tag for the unreleased | |
| 4 | + | # conventional commits. The tag is then built by the shared GoReleaser | |
| 5 | + | # workflow at its exact commit. | |
| 16 | 6 | on: | |
| 17 | 7 | workflow_run: | |
| 18 | 8 | workflows: [ci] | |
| @@ -20,85 +10,60 @@ on: | |||
| 20 | 10 | branches: [main] | |
| 21 | 11 | ||
| 22 | 12 | permissions: | |
| 23 | - | # Needed to create tags and to upload release archives. | |
| 24 | 13 | contents: write | |
| 25 | 14 | ||
| 26 | - | # Only one release job at a time. If two pushes land in quick | |
| 27 | - | # succession, queue them so they don't race on the tag. | |
| 28 | 15 | concurrency: | |
| 29 | 16 | group: release | |
| 30 | 17 | cancel-in-progress: false | |
| 31 | 18 | ||
| 32 | 19 | jobs: | |
| 33 | - | release: | |
| 34 | - | # Only run when ci succeeded and we're on main, and when the head | |
| 35 | - | # commit doesn't opt out via [skip-release]. | |
| 36 | - | if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main' && !contains(github.event.workflow_run.head_commit.message, '[release=skip]') }} | |
| 20 | + | tag: | |
| 21 | + | if: >- | |
| 22 | + | github.event.workflow_run.conclusion == 'success' && | |
| 23 | + | github.event.workflow_run.head_branch == 'main' && | |
| 24 | + | !contains(github.event.workflow_run.head_commit.message, '[release=skip]') | |
| 37 | 25 | runs-on: ubuntu-latest | |
| 26 | + | outputs: | |
| 27 | + | tag: ${{ steps.version.outputs.tag }} | |
| 38 | 28 | steps: | |
| 39 | - | - name: checkout | |
| 29 | + | - name: Check out the verified commit | |
| 40 | 30 | uses: actions/checkout@v4 | |
| 41 | 31 | with: | |
| 42 | - | # Full history so we can read existing tags. | |
| 43 | 32 | fetch-depth: 0 | |
| 44 | - | # Check out the exact sha that ci verified, not whatever the | |
| 45 | - | # branch tip happens to be at this moment (which may have | |
| 46 | - | # advanced since ci finished). | |
| 33 | + | fetch-tags: true | |
| 47 | 34 | ref: ${{ github.event.workflow_run.head_sha }} | |
| 48 | 35 | ||
| 49 | - | - name: compute next version | |
| 50 | - | id: bump | |
| 36 | + | - name: Compute the next release tag | |
| 37 | + | id: version | |
| 51 | 38 | shell: bash | |
| 52 | 39 | run: | | |
| 53 | 40 | set -euo pipefail | |
| 54 | - | # Find the newest vX.Y.Z tag. If the repo has none yet, start | |
| 55 | - | # at v0.0.0 so the first release becomes v0.0.1. | |
| 56 | - | latest=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n1) | |
| 57 | - | if [ -z "$latest" ]; then | |
| 58 | - | latest="v0.0.0" | |
| 59 | - | fi | |
| 60 | - | echo "latest tag: $latest" | |
| 61 | - | ||
| 62 | - | # Strip the v prefix, split on dots, bump the patch. | |
| 63 | - | # After .99 roll over to the next minor (e.g. 0.0.99 -> 0.1.0, | |
| 64 | - | # 0.1.99 -> 0.2.0). Keep major fixed at 0 for now. | |
| 65 | - | ver="${latest#v}" | |
| 66 | - | IFS=. read -r major minor patch <<<"$ver" | |
| 67 | - | if [ "$patch" -ge 99 ]; then | |
| 68 | - | minor=$((minor + 1)) | |
| 69 | - | patch=0 | |
| 70 | - | else | |
| 71 | - | patch=$((patch + 1)) | |
| 41 | + | tag=$(bash .github/scripts/next-release-tag.sh) | |
| 42 | + | if [ -z "$tag" ]; then | |
| 43 | + | echo "No releasable conventional commits for this run." | |
| 44 | + | exit 0 | |
| 72 | 45 | fi | |
| 73 | - | next="v${major}.${minor}.${patch}" | |
| 74 | - | echo "next tag: $next" | |
| 46 | + | echo "Releasing $tag from $(git rev-parse --short HEAD)" | |
| 47 | + | echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| 75 | 48 | ||
| 76 | - | echo "next=$next" >> "$GITHUB_OUTPUT" | |
| 77 | - | echo "version=${next#v}" >> "$GITHUB_OUTPUT" | |
| 78 | - | ||
| 79 | - | - name: create and push tag | |
| 49 | + | - name: Create and push the tag | |
| 50 | + | if: steps.version.outputs.tag != '' | |
| 80 | 51 | env: | |
| 81 | - | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| 52 | + | TAG: ${{ steps.version.outputs.tag }} | |
| 82 | 53 | run: | | |
| 83 | 54 | git config user.name "github-actions[bot]" | |
| 84 | 55 | git config user.email "github-actions[bot]@users.noreply.github.com" | |
| 85 | - | git tag -a "${{ steps.bump.outputs.next }}" \ | |
| 86 | - | -m "release ${{ steps.bump.outputs.next }}" | |
| 87 | - | git push origin "${{ steps.bump.outputs.next }}" | |
| 56 | + | git tag -a "$TAG" -m "release $TAG" | |
| 57 | + | git push origin "$TAG" | |
| 88 | 58 | ||
| 89 | - | - name: set up go | |
| 90 | - | uses: actions/setup-go@v7 | |
| 91 | - | with: | |
| 92 | - | go-version: "1.26" | |
| 93 | - | cache: true | |
| 94 | - | ||
| 95 | - | - name: run goreleaser | |
| 96 | - | uses: goreleaser/goreleaser-action@v6 | |
| 97 | - | with: | |
| 98 | - | distribution: goreleaser | |
| 99 | - | version: latest | |
| 100 | - | args: release --clean | |
| 101 | - | env: | |
| 102 | - | # goreleaser reads the tag we just pushed and builds binaries | |
| 103 | - | # for every (goos, goarch) combination in .goreleaser.yaml. | |
| 104 | - | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| 59 | + | release: | |
| 60 | + | needs: tag | |
| 61 | + | if: needs.tag.outputs.tag != '' | |
| 62 | + | permissions: | |
| 63 | + | contents: write | |
| 64 | + | uses: bnema/gh-actions/.github/workflows/go-release.yml@main | |
| 65 | + | with: | |
| 66 | + | go-version: "1.26" | |
| 67 | + | # The reusable workflow fetches tags before this command, ensuring | |
| 68 | + | # GoReleaser produces artifacts and notes for the newly created tag. | |
| 69 | + | pre-command: git checkout --detach "${{ needs.tag.outputs.tag }}" | |
M .goreleaser.yaml +38 -33
| @@ -1,23 +1,18 @@ | |||
| 1 | 1 | # GoReleaser config — builds cross-platform binaries and GitHub Release | |
| 2 | - | # artifacts for zot. | |
| 3 | - | # | |
| 4 | - | # Triggered by pushing a tag like `v0.1.0`. The release workflow runs | |
| 5 | - | # `goreleaser release --clean`, which compiles every target, packs | |
| 6 | - | # them into `.tar.gz` (Unix) / `.zip` (Windows) archives, generates a | |
| 7 | - | # checksum file, and uploads everything to a GitHub Release. | |
| 8 | - | ||
| 2 | + | # artifacts for zut. The release workflow creates conventional-commit-based | |
| 3 | + | # v0.x tags, then invokes GoReleaser for that immutable tag. | |
| 9 | 4 | version: 2 | |
| 10 | 5 | ||
| 11 | - | project_name: zot | |
| 6 | + | project_name: zut | |
| 12 | 7 | ||
| 13 | 8 | before: | |
| 14 | 9 | hooks: | |
| 15 | 10 | - go mod tidy | |
| 16 | 11 | ||
| 17 | 12 | builds: | |
| 18 | - | - id: zot | |
| 19 | - | main: ./cmd/zot | |
| 20 | - | binary: zot | |
| 13 | + | - id: zut | |
| 14 | + | main: ./cmd/zut | |
| 15 | + | binary: zut | |
| 21 | 16 | env: | |
| 22 | 17 | - CGO_ENABLED=0 | |
| 23 | 18 | goos: [linux, darwin, windows] | |
| @@ -36,59 +31,69 @@ builds: | |||
| 36 | 31 | - -X main.date={{.Date}} | |
| 37 | 32 | ||
| 38 | 33 | archives: | |
| 39 | - | - id: zot | |
| 40 | - | # Human-friendly name: zot_0.1.0_darwin_arm64.tar.gz | |
| 34 | + | - id: zut | |
| 35 | + | # Human-friendly name: zut_0.1.0_darwin_arm64.tar.gz | |
| 41 | 36 | name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" | |
| 42 | 37 | files: | |
| 43 | 38 | - LICENSE | |
| 44 | 39 | - README.md | |
| 45 | 40 | format_overrides: | |
| 46 | 41 | - goos: windows | |
| 47 | - | format: zip | |
| 42 | + | formats: [zip] | |
| 48 | 43 | ||
| 49 | 44 | checksum: | |
| 50 | 45 | name_template: "checksums.txt" | |
| 51 | 46 | algorithm: sha256 | |
| 52 | 47 | ||
| 53 | 48 | snapshot: | |
| 54 | - | # Used by `goreleaser release --snapshot`; lets contributors build | |
| 55 | - | # a local release without pushing a tag. | |
| 56 | 49 | version_template: "{{ incpatch .Version }}-next" | |
| 57 | 50 | ||
| 51 | + | git: | |
| 52 | + | # Generate prerelease changelogs from the previous stable tag. | |
| 53 | + | tag_sort: smartsemver | |
| 54 | + | ||
| 58 | 55 | changelog: | |
| 59 | 56 | sort: asc | |
| 60 | 57 | use: github | |
| 61 | 58 | filters: | |
| 59 | + | # Styling-only commits are neither releasable nor useful release notes. | |
| 62 | 60 | exclude: | |
| 63 | - | - "^docs:" | |
| 64 | - | - "^chore:" | |
| 65 | - | - "^ci:" | |
| 66 | - | - "^test:" | |
| 61 | + | - "^style:" | |
| 67 | 62 | groups: | |
| 68 | - | - title: features | |
| 69 | - | regexp: "^.*feat[(\\w)]*:+.*$" | |
| 63 | + | - title: Added | |
| 64 | + | regexp: "^feat(\\([^)]+\\))?!?:" | |
| 70 | 65 | order: 0 | |
| 71 | - | - title: fixes | |
| 72 | - | regexp: "^.*fix[(\\w)]*:+.*$" | |
| 66 | + | - title: Fixed | |
| 67 | + | regexp: "^fix(\\([^)]+\\))?!?:" | |
| 73 | 68 | order: 1 | |
| 74 | - | - title: other | |
| 69 | + | - title: Changed | |
| 70 | + | regexp: "^(perf|refactor|build)(\\([^)]+\\))?!?:" | |
| 71 | + | order: 2 | |
| 72 | + | - title: Maintenance | |
| 73 | + | regexp: "^(chore|ci)(\\([^)]+\\))?!?:" | |
| 74 | + | order: 3 | |
| 75 | + | - title: Documentation | |
| 76 | + | regexp: "^docs(\\([^)]+\\))?!?:" | |
| 77 | + | order: 4 | |
| 78 | + | - title: Tests | |
| 79 | + | regexp: "^test(\\([^)]+\\))?!?:" | |
| 80 | + | order: 5 | |
| 81 | + | - title: Other changes | |
| 75 | 82 | order: 999 | |
| 76 | 83 | ||
| 77 | 84 | release: | |
| 78 | 85 | github: | |
| 79 | - | owner: patriceckhart | |
| 80 | - | name: zot | |
| 86 | + | owner: bnema | |
| 87 | + | name: zut | |
| 81 | 88 | draft: false | |
| 82 | 89 | prerelease: auto | |
| 83 | - | # Auto-generated release notes include the changelog groups above plus | |
| 84 | - | # an install snippet so the release page itself is a landing page. | |
| 85 | 90 | header: | | |
| 86 | - | ## zot {{ .Tag }} | |
| 91 | + | ## zut {{ .Tag }} | |
| 87 | 92 | ||
| 88 | - | one-liner install: | |
| 93 | + | Install with: | |
| 89 | 94 | ||
| 90 | 95 | ```bash | |
| 91 | - | curl -fsSL https://raw.githubusercontent.com/patriceckhart/zot/main/install.sh | bash | |
| 96 | + | curl -fsSL https://raw.githubusercontent.com/bnema/zut/main/install.sh | bash | |
| 92 | 97 | ``` | |
| 93 | 98 | ||
| 94 | - | or download a binary below, `chmod +x`, and drop it on your `$PATH`. | |
| 99 | + | Or download a binary below, verify it with `checksums.txt`, and add it to your `$PATH`. | |
M AGENTS.md +6 -6
| @@ -1,10 +1,10 @@ | |||
| 1 | - | # Working Agreement for zot | |
| 1 | + | # Working Agreement for zut | |
| 2 | 2 | ||
| 3 | 3 | This file defines how automated coding assistants should work in this repository. Treat it as a practical operating manual, not a substitute for reading the code. | |
| 4 | 4 | ||
| 5 | 5 | ## Product intent | |
| 6 | 6 | ||
| 7 | - | zot is a compact Go coding-agent harness. Changes should preserve its defining properties: | |
| 7 | + | zut is a compact Go coding-agent harness. Changes should preserve its defining properties: | |
| 8 | 8 | ||
| 9 | 9 | - one portable binary | |
| 10 | 10 | - a provider-neutral agent engine | |
| @@ -36,7 +36,7 @@ Put behavior in the package that owns the concern: | |||
| 36 | 36 | | `packages/core` | Agent loop, messages, tool contracts, events, confirmation, sessions, compaction | | |
| 37 | 37 | | `packages/provider` | Provider clients, wire formats, streaming, retries, model metadata and discovery | | |
| 38 | 38 | | `packages/provider/auth` | Login flows, credential lookup, refresh, and storage | | |
| 39 | - | | `packages/agent` | CLI configuration, model selection, runtime assembly, RPC, and zotfiles | | |
| 39 | + | | `packages/agent` | CLI configuration, model selection, runtime assembly, RPC, and zutfiles | | |
| 40 | 40 | | `packages/agent/modes` | Interactive, print, JSON, dialog, bot, and Telegram interfaces | | |
| 41 | 41 | | `packages/agent/extensions` | Extension process lifecycle and host integration | | |
| 42 | 42 | | `packages/agent/extproto` | Extension wire protocol | | |
| @@ -51,7 +51,7 @@ Provider quirks must not leak into `packages/core`. Terminal escape handling and | |||
| 51 | 51 | ||
| 52 | 52 | ## Correctness contracts | |
| 53 | 53 | ||
| 54 | - | Some parts of zot require extra care because small mistakes corrupt state or weaken user protections. | |
| 54 | + | Some parts of zut require extra care because small mistakes corrupt state or weaken user protections. | |
| 55 | 55 | ||
| 56 | 56 | ### Agent events and sessions | |
| 57 | 57 | ||
| @@ -148,13 +148,13 @@ Documentation-only changes do not require the Go test suite unless they alter ge | |||
| 148 | 148 | ||
| 149 | 149 | ## Documentation duties | |
| 150 | 150 | ||
| 151 | - | Update documentation in the same change whenever users, integrators, or extension authors would observe different behavior. Relevant locations include `README.md`, `docs/rpc.md`, `docs/extensions.md`, `docs/skills.md`, `docs/themes.md`, and `docs/zotfiles.md`. | |
| 151 | + | Update documentation in the same change whenever users, integrators, or extension authors would observe different behavior. Relevant locations include `README.md`, `docs/rpc.md`, `docs/extensions.md`, `docs/skills.md`, `docs/themes.md`, and `docs/zutfiles.md`. | |
| 152 | 152 | ||
| 153 | 153 | Examples must match real flags and schemas. State limitations plainly, especially around sandboxing, provider support, compatibility, and platform differences. | |
| 154 | 154 | ||
| 155 | 155 | ## Dependency policy | |
| 156 | 156 | ||
| 157 | - | zot's single-binary design and small footprint are product requirements. | |
| 157 | + | zut's single-binary design and small footprint are product requirements. | |
| 158 | 158 | ||
| 159 | 159 | - Prefer the standard library or existing dependencies. | |
| 160 | 160 | - Explain why a new dependency is necessary before adding it. | |
M CONTRIBUTING.md +9 -9
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # Contributing to zot | |
| 1 | + | # Contributing to zut | |
| 2 | 2 | ||
| 3 | 3 | Contributions are welcome, including bug reports, design feedback, documentation, tests, and code. This guide explains what makes a contribution easy to evaluate and safe to merge. | |
| 4 | 4 | ||
| @@ -8,7 +8,7 @@ Contributions are welcome, including bug reports, design feedback, documentation | |||
| 8 | 8 | ||
| 9 | 9 | Use the bug-report template and provide enough information for someone else to reproduce the problem: | |
| 10 | 10 | ||
| 11 | - | - the output of `zot --version` | |
| 11 | + | - the output of `zut --version` | |
| 12 | 12 | - operating system and terminal | |
| 13 | 13 | - provider and model, when relevant | |
| 14 | 14 | - the smallest sequence of actions that triggers the problem | |
| @@ -35,14 +35,14 @@ Focused typo, documentation, and test improvements can usually go directly to a | |||
| 35 | 35 | ||
| 36 | 36 | ## Design boundaries | |
| 37 | 37 | ||
| 38 | - | zot is intended to remain a lightweight, single-binary application. A contribution should fit that direction rather than increasing scope by default. | |
| 38 | + | zut is intended to remain a lightweight, single-binary application. A contribution should fit that direction rather than increasing scope by default. | |
| 39 | 39 | ||
| 40 | 40 | The major ownership boundaries are: | |
| 41 | 41 | ||
| 42 | 42 | - `packages/core`: provider-independent agent behavior and session structures | |
| 43 | 43 | - `packages/provider`: provider requests, streaming responses, model discovery, retries, and wire-specific behavior | |
| 44 | 44 | - `packages/provider/auth`: credential and login handling | |
| 45 | - | - `packages/agent`: command-line orchestration, configuration, runtime assembly, RPC, and zotfiles | |
| 45 | + | - `packages/agent`: command-line orchestration, configuration, runtime assembly, RPC, and zutfiles | |
| 46 | 46 | - `packages/agent/modes`: interactive and non-interactive user modes | |
| 47 | 47 | - `packages/agent/extensions`, `extproto`, and `ext`: extension hosting, protocol, and SDK | |
| 48 | 48 | - `packages/agent/skills`: reusable instruction discovery | |
| @@ -62,14 +62,14 @@ Use the Go version declared in `go.mod`, then build from the repository root: | |||
| 62 | 62 | ```sh | |
| 63 | 63 | go mod download | |
| 64 | 64 | make build | |
| 65 | - | ./bin/zot --version | |
| 65 | + | ./bin/zut --version | |
| 66 | 66 | ``` | |
| 67 | 67 | ||
| 68 | 68 | Useful commands: | |
| 69 | 69 | ||
| 70 | 70 | ```sh | |
| 71 | 71 | make help # list the important developer commands | |
| 72 | - | make build # compile ./bin/zot as 0.0.0-dev | |
| 72 | + | make build # compile ./bin/zut as 0.0.0-dev | |
| 73 | 73 | make run # build and start the interactive application | |
| 74 | 74 | make install # install the current checkout with go install | |
| 75 | 75 | make go-install # install a published module version | |
| @@ -79,7 +79,7 @@ make lint # run golangci-lint, go vet, and verify gofmt | |||
| 79 | 79 | make lint-install # install the pinned golangci-lint version | |
| 80 | 80 | ``` | |
| 81 | 81 | ||
| 82 | - | Tests must not depend on real provider credentials, paid model calls, a developer's global zot state, or a specific terminal. Use local HTTP test servers, fixtures, temporary directories, and synthetic credentials. | |
| 82 | + | Tests must not depend on real provider credentials, paid model calls, a developer's global zut state, or a specific terminal. Use local HTTP test servers, fixtures, temporary directories, and synthetic credentials. | |
| 83 | 83 | ||
| 84 | 84 | ## Making a change | |
| 85 | 85 | ||
| @@ -150,7 +150,7 @@ If a validation command cannot pass because of an unrelated repository or enviro | |||
| 150 | 150 | Open the pull request against `main` and include: | |
| 151 | 151 | ||
| 152 | 152 | - the problem being addressed | |
| 153 | - | - the chosen solution and why it belongs in zot | |
| 153 | + | - the chosen solution and why it belongs in zut | |
| 154 | 154 | - tests added or updated | |
| 155 | 155 | - commands used for verification | |
| 156 | 156 | - user-visible, compatibility, and platform effects | |
| @@ -170,7 +170,7 @@ Choose the document that owns the public surface: | |||
| 170 | 170 | - `docs/extensions.md` for extension authors | |
| 171 | 171 | - `docs/skills.md` for reusable skills | |
| 172 | 172 | - `docs/themes.md` for theme files | |
| 173 | - | - `docs/zotfiles.md` for portable agents | |
| 173 | + | - `docs/zutfiles.md` for portable agents | |
| 174 | 174 | ||
| 175 | 175 | Examples should be runnable and limitations should be stated directly. | |
| 176 | 176 | ||
M Makefile +5 -5
| @@ -3,8 +3,8 @@ | |||
| 3 | 3 | .DEFAULT_GOAL := help | |
| 4 | 4 | ||
| 5 | 5 | GO ?= go | |
| 6 | - | PKG ?= ./cmd/zot | |
| 7 | - | BINARY ?= zot | |
| 6 | + | PKG ?= ./cmd/zut | |
| 7 | + | BINARY ?= zut | |
| 8 | 8 | BIN_DIR ?= bin | |
| 9 | 9 | DEV_VERSION ?= 0.0.0-dev | |
| 10 | 10 | VERSION ?= $(DEV_VERSION) | |
| @@ -17,7 +17,7 @@ LDFLAGS := -s -w -X main.version=$(VERSION) | |||
| 17 | 17 | ||
| 18 | 18 | help: | |
| 19 | 19 | @printf '%s\n' \ | |
| 20 | - | 'build build ./bin/zot' \ | |
| 20 | + | 'build build ./bin/zut' \ | |
| 21 | 21 | 'install install the current checkout with go install' \ | |
| 22 | 22 | 'go-install install a published module version (GO_INSTALL_VERSION=latest)' \ | |
| 23 | 23 | 'test run all tests with the race detector' \ | |
| @@ -27,7 +27,7 @@ help: | |||
| 27 | 27 | 'lint-install install golangci-lint $(GOLANGCI_LINT_VERSION)' \ | |
| 28 | 28 | 'fmt format Go source files' \ | |
| 29 | 29 | 'check run test-fast and lint' \ | |
| 30 | - | 'run build and run ./bin/zot (pass ARGS="...")' \ | |
| 30 | + | 'run build and run ./bin/zut (pass ARGS="...")' \ | |
| 31 | 31 | 'clean remove build output' \ | |
| 32 | 32 | 'release cross-build release binaries (set VERSION=...)' | |
| 33 | 33 | ||
| @@ -41,7 +41,7 @@ install: | |||
| 41 | 41 | ||
| 42 | 42 | # Install a published/module version, rather than the current checkout. | |
| 43 | 43 | go-install: | |
| 44 | - | $(GO) install github.com/patriceckhart/zot/cmd/zot@$(GO_INSTALL_VERSION) | |
| 44 | + | $(GO) install github.com/bnema/zut/cmd/zut@$(GO_INSTALL_VERSION) | |
| 45 | 45 | ||
| 46 | 46 | test: | |
| 47 | 47 | $(GO) test -race ./... | |
M README.md +191 -188
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | <div align="center"> | |
| 2 | - | <a href="https://www.zot.sh"> | |
| 3 | - | <img src="packages/provider/auth/assets/zot-logo.png" alt="zot coding agent harness" width="130" height="130" /> | |
| 2 | + | <a href="https://github.com/bnema/zut"> | |
| 3 | + | <img src="packages/provider/auth/assets/zut-logo.png" alt="zut coding agent harness" width="130" height="130" /> | |
| 4 | 4 | </a> | |
| 5 | 5 | </div> | |
| 6 | 6 | <br> | |
| @@ -10,9 +10,12 @@ | |||
| 10 | 10 | <img alt="30+ providers" src="https://img.shields.io/badge/providers-30+-34E2EA"> | |
| 11 | 11 | </p> | |
| 12 | 12 | <p align="center"> | |
| 13 | - | <a href="https://www.zot.sh">zot.sh</a> | |
| 13 | + | <a href="https://github.com/bnema/zut">github.com/bnema/zut</a> | |
| 14 | 14 | </p> | |
| 15 | 15 | ||
| 16 | + | > [!INFO] | |
| 17 | + | > **Fork notice:** `zut` is an independently maintained fork of [zot](https://github.com/patriceckhart/zot). It retains the upstream MIT license and copyright notice; see [LICENSE](LICENSE). | |
| 18 | + | ||
| 16 | 19 | ## What is it? | |
| 17 | 20 | ||
| 18 | 21 | Yet another coding agent harness, lightweight and written (vibe-slopped) in go. | |
| @@ -22,50 +25,50 @@ Yet another coding agent harness, lightweight and written (vibe-slopped) in go. | |||
| 22 | 25 | - five core built-in tools (read, write, edit, bash, and lsp); the conditional `skill` tool is available when skills are enabled. | |
| 23 | 26 | - three run modes (interactive tui, print, json). | |
| 24 | 27 | - built-in telegram bot. | |
| 25 | - | - extensions in any language via subprocess + json-rpc. None installed by default; opt in with `zot ext install` or `zot --ext`. See [docs/extensions.md](docs/extensions.md). | |
| 28 | + | - extensions in any language via subprocess + json-rpc. None installed by default; opt in with `zut ext install` or `zut --ext`. See [docs/extensions.md](docs/extensions.md). | |
| 26 | 29 | - user and extension themes via JSON; see [docs/themes.md](docs/themes.md). | |
| 27 | 30 | - standing instructions via `AGENTS.md` files (global and per-project); see [Persistent instructions](#persistent-instructions-agentsmd). | |
| 28 | 31 | - reusable instructions via `SKILL.md` files; see [docs/skills.md](docs/skills.md). | |
| 29 | 32 | - named subagent profiles from `~/.agents/agents/*.md`; see [docs/subagents.md](docs/subagents.md). | |
| 30 | - | - portable agents from local directories, `.zot` files, or temporary public GitHub downloads; see [docs/zotfiles.md](docs/zotfiles.md). | |
| 33 | + | - portable agents from local directories, `.zut` files, or temporary public GitHub downloads; see [docs/zutfiles.md](docs/zutfiles.md). | |
| 31 | 34 | ||
| 32 | 35 | ## Install | |
| 33 | 36 | ||
| 34 | 37 | ### One-liner (macOS, Linux) | |
| 35 | 38 | ||
| 36 | 39 | ```bash | |
| 37 | - | curl -fsSL https://www.zot.sh/install.sh | bash | |
| 40 | + | curl -fsSL https://raw.githubusercontent.com/bnema/zut/main/install.sh | bash | |
| 38 | 41 | ``` | |
| 39 | 42 | ||
| 40 | 43 | Detects your OS and architecture, downloads the latest release from GitHub, verifies the SHA-256 against the release's `checksums.txt`, extracts the binary, and drops it in `/usr/local/bin`, `~/.local/bin`, or `~/bin`, whichever is writable first. Pass a version or prefix to pin: | |
| 41 | 44 | ||
| 42 | 45 | ```bash | |
| 43 | - | curl -fsSL https://www.zot.sh/install.sh | bash -s -- v0.0.1 ~/bin | |
| 46 | + | curl -fsSL https://raw.githubusercontent.com/bnema/zut/main/install.sh | bash -s -- v0.1.0 ~/bin | |
| 44 | 47 | ``` | |
| 45 | 48 | ||
| 46 | 49 | ### One-liner (Windows, PowerShell) | |
| 47 | 50 | ||
| 48 | 51 | ```powershell | |
| 49 | - | iwr -useb https://www.zot.sh/install.ps1 | iex | |
| 52 | + | iwr -useb https://raw.githubusercontent.com/bnema/zut/main/install.ps1 | iex | |
| 50 | 53 | ``` | |
| 51 | 54 | ||
| 52 | - | Drops `zot.exe` into `$HOME\bin` and adds it to the user PATH if missing. Open a fresh terminal afterwards. | |
| 55 | + | Drops `zut.exe` into `$HOME\bin` and adds it to the user PATH if missing. Open a fresh terminal afterwards. | |
| 53 | 56 | ||
| 54 | 57 | ### go install | |
| 55 | 58 | ||
| 56 | 59 | ```bash | |
| 57 | - | go install github.com/patriceckhart/zot/cmd/zot@latest | |
| 60 | + | go install github.com/bnema/zut/cmd/zut@latest | |
| 58 | 61 | ``` | |
| 59 | 62 | ||
| 60 | - | The installed binary reports the tagged module version and supports `zot update`. | |
| 63 | + | The installed binary reports the tagged module version and supports `zut update`. | |
| 61 | 64 | ||
| 62 | 65 | ### From source | |
| 63 | 66 | ||
| 64 | 67 | ```bash | |
| 65 | - | git clone https://github.com/patriceckhart/zot | |
| 66 | - | cd zot | |
| 68 | + | git clone https://github.com/bnema/zut | |
| 69 | + | cd zut | |
| 67 | 70 | make help # list the important developer commands | |
| 68 | - | make build # produces ./bin/zot (0.0.0-dev by default) | |
| 71 | + | make build # produces ./bin/zut (0.0.0-dev by default) | |
| 69 | 72 | make install # install the current checkout with go install | |
| 70 | 73 | make go-install # install the latest published module version | |
| 71 | 74 | ``` | |
| @@ -76,23 +79,23 @@ development build. | |||
| 76 | 79 | ||
| 77 | 80 | ### Prebuilt binaries | |
| 78 | 81 | ||
| 79 | - | Every release on the [releases page](https://github.com/patriceckhart/zot/releases) ships archives for Linux, macOS, and Windows on amd64 and arm64 (except windows/arm64), plus a `checksums.txt` file. Download, verify, `chmod +x`, and drop on your `$PATH`. | |
| 82 | + | Every release on the [releases page](https://github.com/bnema/zut/releases) ships archives for Linux, macOS, and Windows on amd64 and arm64 (except windows/arm64), plus a `checksums.txt` file. Download, verify, `chmod +x`, and drop on your `$PATH`. | |
| 80 | 83 | ||
| 81 | 84 | ## Authenticate | |
| 82 | 85 | ||
| 83 | - | The easiest way is to just run `zot` and type `/login`. The TUI opens even without credentials and walks you through a browser-based login flow. | |
| 86 | + | The easiest way is to just run `zut` and type `/login`. The TUI opens even without credentials and walks you through a browser-based login flow. | |
| 84 | 87 | ||
| 85 | 88 | ### Credential lookup order | |
| 86 | 89 | ||
| 87 | 90 | 1. `--api-key` flag | |
| 88 | 91 | 2. provider-specific env var (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `KIMI_API_KEY`, `MOONSHOT_API_KEY`, `DEEPSEEK_API_KEY`, `GEMINI_API_KEY`, `GOOGLE_API_KEY`, `GROQ_API_KEY`, `OPENROUTER_API_KEY`, `MISTRAL_API_KEY`, `XAI_API_KEY`, `CEREBRAS_API_KEY`, `TOGETHER_API_KEY`, `HF_TOKEN`, `ZAI_API_KEY`, `XIAOMI_API_KEY`, `MINIMAX_API_KEY`, `FIREWORKS_API_KEY`, `AI_GATEWAY_API_KEY`, `COPILOT_GITHUB_TOKEN`, `GITHUB_COPILOT_TOKEN`, and others for provider-specific backends) | |
| 89 | - | 3. `$ZOT_HOME/auth.json` (API key or OAuth token; mode 0600) | |
| 92 | + | 3. `$ZUT_HOME/auth.json` (API key or OAuth token; mode 0600) | |
| 90 | 93 | ||
| 91 | - | `$ZOT_HOME` defaults to: | |
| 92 | - | - All platforms: `$XDG_STATE_HOME/zot` when `XDG_STATE_HOME` is set | |
| 93 | - | - macOS fallback: `~/Library/Application Support/zot` | |
| 94 | - | - Linux fallback: `~/.local/state/zot` | |
| 95 | - | - Windows fallback: `%LOCALAPPDATA%\zot` | |
| 94 | + | `$ZUT_HOME` defaults to: | |
| 95 | + | - All platforms: `$XDG_STATE_HOME/zut` when `XDG_STATE_HOME` is set | |
| 96 | + | - macOS fallback: `~/Library/Application Support/zut` | |
| 97 | + | - Linux fallback: `~/.local/state/zut` | |
| 98 | + | - Windows fallback: `%LOCALAPPDATA%\zut` | |
| 96 | 99 | ||
| 97 | 100 | ### API keys from commands | |
| 98 | 101 | ||
| @@ -112,37 +115,37 @@ To keep an API key in a password manager instead of `auth.json`, configure an `a | |||
| 112 | 115 | ||
| 113 | 116 | For a provider added through `models.json`, put the same credential object under `additional_api_key_creds` using its provider ID. `program` is executed directly, without a shell, so each argument must be a separate `args` entry. `timeout_ms` is optional and defaults to 120 seconds. | |
| 114 | 117 | ||
| 115 | - | zot runs the command only when that provider is selected, not while checking login status or refreshing model catalogs in the background. Successful output is cached in memory for the rest of the zot process and is never written to disk. The command must print one non-empty line to stdout; zot removes trailing CR/LF characters, limits output to 64 KiB, and does not include command output in errors. Saving a normal key through `/login` replaces the command configuration, and `/logout` removes it. | |
| 118 | + | zut runs the command only when that provider is selected, not while checking login status or refreshing model catalogs in the background. Successful output is cached in memory for the rest of the zut process and is never written to disk. The command must print one non-empty line to stdout; zut removes trailing CR/LF characters, limits output to 64 KiB, and does not include command output in errors. Saving a normal key through `/login` replaces the command configuration, and `/logout` removes it. | |
| 116 | 119 | ||
| 117 | - | Treat `auth.json` as executable configuration: anyone who can modify it can cause zot to run a program under your user account. zot does not interpret `!` prefixes or execute command strings through a shell. | |
| 120 | + | Treat `auth.json` as executable configuration: anyone who can modify it can cause zut to run a program under your user account. zut does not interpret `!` prefixes or execute command strings through a shell. | |
| 118 | 121 | ||
| 119 | 122 | ### `/login` flow | |
| 120 | 123 | ||
| 121 | - | Run `zot` and type `/login`. Pick one of two methods: | |
| 124 | + | Run `zut` and type `/login`. Pick one of two methods: | |
| 122 | 125 | ||
| 123 | - | - **API key**: a small local web server starts on `127.0.0.1:<free-port>`, your browser opens a form, you pick a provider from the full API-key provider list, paste the key, and zot saves it to `auth.json` if accepted. Providers with a lightweight model-list endpoint are probed before saving; provider backends that need extra project/account env vars are saved directly. | |
| 126 | + | - **API key**: a small local web server starts on `127.0.0.1:<free-port>`, your browser opens a form, you pick a provider from the full API-key provider list, paste the key, and zut saves it to `auth.json` if accepted. Providers with a lightweight model-list endpoint are probed before saving; provider backends that need extra project/account env vars are saved directly. | |
| 124 | 127 | - **Subscription**: use your Claude Pro/Max, ChatGPT Plus/Pro, Kimi Code, SuperGrok/X Premium, or GitHub Copilot subscription. DeepSeek and Google Gemini do **not** have a subscription login path. For those, use the API-key flow. | |
| 125 | 128 | - Anthropic and OpenAI pin the browser callback to fixed provider-specific ports (`localhost:53692` for Anthropic, `localhost:1455` for OpenAI) because those are the only ports their auth servers will redirect to. | |
| 126 | 129 | - Anthropic uses the Claude Code OAuth flow. Messages go to `api.anthropic.com` with a bearer token and the Claude Code identity headers. | |
| 127 | 130 | - OpenAI uses the Codex CLI OAuth flow. Messages go to `chatgpt.com/backend-api/codex/responses` with the `chatgpt-account-id` extracted from the returned id_token. | |
| 128 | - | - Kimi uses the Kimi Code device-code OAuth flow. zot opens the verification URL, polls until you approve it in the browser, then sends messages to `api.kimi.com/coding/v1` with the Kimi Code identity headers. | |
| 129 | - | - xAI uses a device-code OAuth flow. zot opens a prefilled authorization URL, polls for approval, and uses the resulting token with the xAI API. | |
| 130 | - | - GitHub Copilot uses GitHub's device-code login flow. zot stores the GitHub access token and exchanges it for short-lived Copilot inference tokens on demand. | |
| 131 | + | - Kimi uses the Kimi Code device-code OAuth flow. zut opens the verification URL, polls until you approve it in the browser, then sends messages to `api.kimi.com/coding/v1` with the Kimi Code identity headers. | |
| 132 | + | - xAI uses a device-code OAuth flow. zut opens a prefilled authorization URL, polls for approval, and uses the resulting token with the xAI API. | |
| 133 | + | - GitHub Copilot uses GitHub's device-code login flow. zut stores the GitHub access token and exchanges it for short-lived Copilot inference tokens on demand. | |
| 131 | 134 | ||
| 132 | 135 | > **Note on subscription login.** The OAuth client IDs used are the ones published in Anthropic's Claude Code CLI, OpenAI's Codex CLI, Kimi Code CLI, xAI's device flow, and GitHub Copilot's device-code flow. Reusing them from a third-party tool may be against their terms of service and may be revoked at any time. Use it at your own risk; the API-key flow is the safe default. | |
| 133 | 136 | ||
| 134 | 137 | ### Token refresh | |
| 135 | 138 | ||
| 136 | - | OAuth access tokens are short-lived (Anthropic ~8h, OpenAI ~30d; Kimi, xAI, and GitHub Copilot also use refresh/exchange flows). zot refreshes or exchanges them automatically: | |
| 139 | + | OAuth access tokens are short-lived (Anthropic ~8h, OpenAI ~30d; Kimi, xAI, and GitHub Copilot also use refresh/exchange flows). zut refreshes or exchanges them automatically: | |
| 137 | 140 | ||
| 138 | - | - At every credential lookup, zot checks the stored `expiry` and, if past it (with a 60s safety margin), hits the provider's `oauth/token` endpoint with the stored `refresh_token`, persists the new `access_token`, `refresh_token`, and `expiry` back to `auth.json`, and hands the fresh token to the client. | |
| 141 | + | - At every credential lookup, zut checks the stored `expiry` and, if past it (with a 60s safety margin), hits the provider's `oauth/token` endpoint with the stored `refresh_token`, persists the new `access_token`, `refresh_token`, and `expiry` back to `auth.json`, and hands the fresh token to the client. | |
| 139 | 142 | - The telegram bridge additionally refreshes once per turn so a bot that runs for days keeps working without manual intervention. | |
| 140 | 143 | - If the refresh itself fails (the `refresh_token` was revoked, or the account was logged out everywhere), the error bubbles up to the caller: the TUI shows it in the status line, the bot replies with it in your DM. Run `/login` to get a fresh token pair. | |
| 141 | 144 | ||
| 142 | - | All data lives under `$ZOT_HOME`: | |
| 145 | + | All data lives under `$ZUT_HOME`: | |
| 143 | 146 | ||
| 144 | 147 | ``` | |
| 145 | - | $ZOT_HOME/ | |
| 148 | + | $ZUT_HOME/ | |
| 146 | 149 | ├── config.json # last-used provider/model/theme and persistent settings | |
| 147 | 150 | ├── auth.json # api keys and oauth tokens (mode 0600) | |
| 148 | 151 | ├── sessions/ # jsonl transcripts, one dir per cwd | |
| @@ -155,24 +158,24 @@ $ZOT_HOME/ | |||
| 155 | 158 | └── logs/ # app log files | |
| 156 | 159 | ``` | |
| 157 | 160 | ||
| 158 | - | Drop a `SYSTEM.md` in `$ZOT_HOME` to replace the built-in identity for every run. Existing append addenda, including `AGENTS.md`, skills, and enabled Ponytail coding mode, remain layered on top. `--system-prompt` wins per invocation unless a selected replace-mode subagent profile supplies the child's identity. Delete the file to revert to the default identity. | |
| 161 | + | Drop a `SYSTEM.md` in `$ZUT_HOME` to replace the built-in identity for every run. Existing append addenda, including `AGENTS.md`, skills, and enabled Ponytail coding mode, remain layered on top. `--system-prompt` wins per invocation unless a selected replace-mode subagent profile supplies the child's identity. Delete the file to revert to the default identity. | |
| 159 | 162 | ||
| 160 | - | Ponytail coding mode is enabled by default, including when an existing `config.json` does not contain a `ponytail_enabled` field. When disabled, its compact guidance is omitted from resolved system prompts; when enabled, the guidance tells the model to apply itself only to engineering work. The setting persists its explicit on/off choice in `$ZOT_HOME/config.json` and can be changed from `/settings`. It is advisory guidance only: it does not change tool permissions, confirmations, jail behavior, or sandboxing. | |
| 163 | + | Ponytail coding mode is enabled by default, including when an existing `config.json` does not contain a `ponytail_enabled` field. When disabled, its compact guidance is omitted from resolved system prompts; when enabled, the guidance tells the model to apply itself only to engineering work. The setting persists its explicit on/off choice in `$ZUT_HOME/config.json` and can be changed from `/settings`. It is advisory guidance only: it does not change tool permissions, confirmations, jail behavior, or sandboxing. | |
| 161 | 164 | ||
| 162 | 165 | A selected subagent profile with `systemPromptMode: replace` supplies the child's base identity even when `--system-prompt` was provided for the parent run. Global append addenda, including enabled Ponytail guidance, still follow their normal inclusion rules. | |
| 163 | 166 | ||
| 164 | 167 | ## Persistent instructions (AGENTS.md) | |
| 165 | 168 | ||
| 166 | - | Use `AGENTS.md` to give zot standing instructions that layer **on top of** the default system prompt, without replacing it. This is the friendliest way to shape behavior (for example, taming local models that jump straight to code edits) because it adds guidance rather than taking over the base identity the way `SYSTEM.md` does. | |
| 169 | + | Use `AGENTS.md` to give zut standing instructions that layer **on top of** the default system prompt, without replacing it. This is the friendliest way to shape behavior (for example, taming local models that jump straight to code edits) because it adds guidance rather than taking over the base identity the way `SYSTEM.md` does. | |
| 167 | 170 | ||
| 168 | - | zot discovers `AGENTS.md` files automatically at startup and loads them in this order: | |
| 171 | + | zut discovers `AGENTS.md` files automatically at startup and loads them in this order: | |
| 169 | 172 | ||
| 170 | - | 1. `$ZOT_HOME/AGENTS.md` (global, machine-wide instructions that apply to every project). | |
| 173 | + | 1. `$ZUT_HOME/AGENTS.md` (global, machine-wide instructions that apply to every project). | |
| 171 | 174 | 2. Every `AGENTS.md` from the filesystem root down to the current working directory. More specific (deeper) files may override earlier ones. This includes `~/AGENTS.md` when the working directory is inside your home directory. | |
| 172 | 175 | ||
| 173 | - | All discovered files are appended to the prompt in that order, so a global baseline can be refined per project. To list the active Zotfile agent, loaded context paths, extensions, and user-installed skills above the interactive transcript, enable **show loaded resources at startup** in `/settings`. The agent section appears only for `zot run`, the setting is off by default, built-in skills are omitted, and the displayed lists are not sent to the model or stored in the session transcript. | |
| 176 | + | All discovered files are appended to the prompt in that order, so a global baseline can be refined per project. To list the active Zutfile agent, loaded context paths, extensions, and user-installed skills above the interactive transcript, enable **show loaded resources at startup** in `/settings`. The agent section appears only for `zut run`, the setting is off by default, built-in skills are omitted, and the displayed lists are not sent to the model or stored in the session transcript. | |
| 174 | 177 | ||
| 175 | - | For a general, non-project-specific instruction set, put your rules in `$ZOT_HOME/AGENTS.md`, for example: | |
| 178 | + | For a general, non-project-specific instruction set, put your rules in `$ZUT_HOME/AGENTS.md`, for example: | |
| 176 | 179 | ||
| 177 | 180 | ```markdown | |
| 178 | 181 | Treat questions and discussions as requests for explanation. Do not edit files or run tools unless explicitly asked to make a change. Ask before modifying code. | |
| @@ -182,30 +185,30 @@ Treat questions and discussions as requests for explanation. Do not edit files o | |||
| 182 | 185 | ||
| 183 | 186 | | Mechanism | Scope | Effect | | |
| 184 | 187 | |---|---|---| | |
| 185 | - | | `$ZOT_HOME/AGENTS.md` | global, every run | appended to the default prompt | | |
| 188 | + | | `$ZUT_HOME/AGENTS.md` | global, every run | appended to the default prompt | | |
| 186 | 189 | | `./AGENTS.md` (and parent dirs) | project | appended to the default prompt | | |
| 187 | - | | `$ZOT_HOME/SYSTEM.md` | global, every run | replaces the built-in identity; append addenda remain | | |
| 190 | + | | `$ZUT_HOME/SYSTEM.md` | global, every run | replaces the built-in identity; append addenda remain | | |
| 188 | 191 | | `--append-system-prompt <text>` | single run | appends for one invocation (repeatable) | | |
| 189 | 192 | | `--system-prompt <text>` | single run | replaces the built-in identity for one invocation; append addenda remain | | |
| 190 | 193 | ||
| 191 | - | > **Note:** zot does not read a `CLAUDE.md` instruction file. The only Claude-compatible thing it picks up is skills under `.claude/skills/`. If you are migrating from Claude Code, move that content into `AGENTS.md` (global or per-project) and zot will use it. | |
| 194 | + | > **Note:** zut does not read a `CLAUDE.md` instruction file. The only Claude-compatible thing it picks up is skills under `.claude/skills/`. If you are migrating from Claude Code, move that content into `AGENTS.md` (global or per-project) and zut will use it. | |
| 192 | 195 | ||
| 193 | 196 | ## Changelog on update | |
| 194 | 197 | ||
| 195 | - | The first time you launch a newer zot binary, the TUI shows the GitHub release notes once in a dismissible overlay. Press any key to close. The version is recorded in `config.json`'s `last_changelog_shown` so the same release notes never reappear. Fresh installs don't see a changelog (no upgrade has happened yet). Development builds (`0.0.0-dev`) skip both the update and changelog requests and print `dev version detected; skipping update check`. For release builds, the fetch is best-effort: a network failure or a missing release page silently skips, with another attempt on the next launch. | |
| 198 | + | The first time you launch a newer zut binary, the TUI shows the GitHub release notes once in a dismissible overlay. Press any key to close. The version is recorded in `config.json`'s `last_changelog_shown` so the same release notes never reappear. Fresh installs don't see a changelog (no upgrade has happened yet). Development builds (`0.0.0-dev`) skip both the update and changelog requests and print `dev version detected; skipping update check`. For release builds, the fetch is best-effort: a network failure or a missing release page silently skips, with another attempt on the next launch. | |
| 196 | 199 | ||
| 197 | 200 | ## Usage | |
| 198 | 201 | ||
| 199 | 202 | ```bash | |
| 200 | - | zot # interactive tui | |
| 201 | - | zot "fix the failing test" # tui, pre-filled prompt | |
| 202 | - | zot -p "list all go files" # print final text, exit | |
| 203 | - | zot -p --stats stats.json "task" # print final text and write generation stats | |
| 204 | - | zot --json "refactor main.go" # newline-delimited json events, exit | |
| 205 | - | zot --continue # resume the most recent session for this cwd | |
| 206 | - | zot --resume # pick a session to resume | |
| 207 | - | zot --list-models # show supported models | |
| 208 | - | zot --help | |
| 203 | + | zut # interactive tui | |
| 204 | + | zut "fix the failing test" # tui, pre-filled prompt | |
| 205 | + | zut -p "list all go files" # print final text, exit | |
| 206 | + | zut -p --stats stats.json "task" # print final text and write generation stats | |
| 207 | + | zut --json "refactor main.go" # newline-delimited json events, exit | |
| 208 | + | zut --continue # resume the most recent session for this cwd | |
| 209 | + | zut --resume # pick a session to resume | |
| 210 | + | zut --list-models # show supported models | |
| 211 | + | zut --help | |
| 209 | 212 | ``` | |
| 210 | 213 | ||
| 211 | 214 | Print-mode stats contain `provider`, `model`, `prompt_tokens`, `reasoning_tokens`, `generated_output_tokens`, and `elapsed_ms`. Counts cover all model turns triggered by the prompt, including tool loops. Prompt tokens include cache reads and writes. `reasoning_tokens` is `null` when the provider does not report a separate count; in that case `generated_output_tokens` is the provider's total output count and may include reasoning. Elapsed time covers the agent run, not startup and credential resolution. The file is written only after a successful run. | |
| @@ -219,7 +222,7 @@ Print-mode stats contain `provider`, `model`, `prompt_tokens`, `reasoning_tokens | |||
| 219 | 222 | | `--api-key <key>` | Override the API key. | | |
| 220 | 223 | | `--base-url <url>` | Override the provider base URL (tests, self-hosted). | | |
| 221 | 224 | | `--insecure` | Skip TLS certificate verification for the explicit `--base-url` endpoint or a `baseUrl` defined for a user model in `models.json` (self-signed local/internal inference servers). Built-in providers, auth, and model discovery keep normal TLS verification. | | |
| 222 | - | | `--system-prompt <text>` | Replace the default system prompt for this run (also overrides `$ZOT_HOME/SYSTEM.md`). | | |
| 225 | + | | `--system-prompt <text>` | Replace the default system prompt for this run (also overrides `$ZUT_HOME/SYSTEM.md`). | | |
| 223 | 226 | | `--append-system-prompt <text>` | Append text to the system prompt (repeatable). | | |
| 224 | 227 | | `--reasoning off\|minimum\|low\|medium\|high\|xhigh\|max` | Set the reasoning level on supported models (default: off). `max` is a separate opt-in tier above `xhigh`. | | |
| 225 | 228 | | `--stats <path>` | With `-p`/`--print`, write generation statistics as JSON. | | |
| @@ -242,7 +245,7 @@ Print-mode stats contain `provider`, `model`, `prompt_tokens`, `reasoning_tokens | |||
| 242 | 245 | - `read`: read text files, or inline images (PNG, JPEG, GIF, WebP). | |
| 243 | 246 | - `write`: create or overwrite files, making parent directories as needed. | |
| 244 | 247 | - `edit`: one or more exact-match replacements in an existing file. | |
| 245 | - | - `bash`: run a command in the session cwd with merged stdout/stderr and a timeout. On Unix, zot uses `/bin/bash -c` when available, then `bash -c` from `PATH`, and falls back to POSIX `/bin/sh -c` when Bash is unavailable. On Windows, it uses `cmd /C`. macOS ships Bash 3.2 by default, so newer Bash features may be unavailable. | |
| 248 | + | - `bash`: run a command in the session cwd with merged stdout/stderr and a timeout. On Unix, zut uses `/bin/bash -c` when available, then `bash -c` from `PATH`, and falls back to POSIX `/bin/sh -c` when Bash is unavailable. On Windows, it uses `cmd /C`. macOS ships Bash 3.2 by default, so newer Bash features may be unavailable. | |
| 246 | 249 | - `lsp`: query configured language servers and linters for diagnostics, definitions, references, hover information, symbols, renames, code actions, capabilities, and raw protocol requests. LSP servers use stdio JSON-RPC; project `lsp.json` files can add or override servers and CLI linters. Diagnostics are bounded, sorted, deduplicated by path/severity/code/start position, and repeated issues are grouped before they reach the model. See [docs/lsp.md](docs/lsp.md). | |
| 247 | 250 | ||
| 248 | 251 | When the sandbox is on (see `/jail`), filesystem tools and LSP workspace edits refuse paths outside the session cwd. | |
| @@ -250,30 +253,30 @@ When the sandbox is on (see `/jail`), filesystem tools and LSP workspace edits r | |||
| 250 | 253 | ## Modes | |
| 251 | 254 | ||
| 252 | 255 | - **Interactive** (default): chat TUI with streaming output, spinner, cost meter, slash commands. | |
| 253 | - | - **Print**: `zot -p "prompt"` runs the agent to completion and writes only the final assistant text to stdout. | |
| 254 | - | - **Stream**: `zot --stream "prompt"` runs without the TUI and writes assistant text to stdout as it arrives. Tool activity goes to stderr. | |
| 255 | - | - **JSON**: `zot --json "prompt"` emits one JSON object per agent event to stdout, newline-delimited. The schema is documented in [docs/rpc.md](docs/rpc.md). | |
| 256 | - | - **RPC**: `zot rpc` runs as a long-lived child process; commands in on stdin, events and responses out on stdout, both as NDJSON. Designed for embedding zot in third-party apps written in any language. See [docs/rpc.md](docs/rpc.md) for the wire schema and `examples/rpc/{python,node,shell,go}` for working clients. | |
| 256 | + | - **Print**: `zut -p "prompt"` runs the agent to completion and writes only the final assistant text to stdout. | |
| 257 | + | - **Stream**: `zut --stream "prompt"` runs without the TUI and writes assistant text to stdout as it arrives. Tool activity goes to stderr. | |
| 258 | + | - **JSON**: `zut --json "prompt"` emits one JSON object per agent event to stdout, newline-delimited. The schema is documented in [docs/rpc.md](docs/rpc.md). | |
| 259 | + | - **RPC**: `zut rpc` runs as a long-lived child process; commands in on stdin, events and responses out on stdout, both as NDJSON. Designed for embedding zut in third-party apps written in any language. See [docs/rpc.md](docs/rpc.md) for the wire schema and `examples/rpc/{python,node,shell,go}` for working clients. | |
| 257 | 260 | ||
| 258 | - | ## zotfile agents | |
| 261 | + | ## zutfile agents | |
| 259 | 262 | ||
| 260 | - | A zotfile packages an agent's instructions, skills, requirements, and enforced tool permissions as a shareable agent. Run one from a local directory, a packed `.zot` artifact, a short name, or directly from a public GitHub repository: | |
| 263 | + | A zutfile packages an agent's instructions, skills, requirements, and enforced tool permissions as a shareable agent. Run one from a local directory, a packed `.zut` artifact, a short name, or directly from a public GitHub repository: | |
| 261 | 264 | ||
| 262 | 265 | ```bash | |
| 263 | - | zot run ./my-agent | |
| 264 | - | zot run ./my-agent.zot | |
| 265 | - | zot run frkr/zot-archify | |
| 266 | - | zot run acme/agents/code-reviewer --cwd /path/to/project | |
| 266 | + | zut run ./my-agent | |
| 267 | + | zut run ./my-agent.zut | |
| 268 | + | zut run frkr/zot-archify | |
| 269 | + | zut run acme/agents/code-reviewer --cwd /path/to/project | |
| 267 | 270 | ``` | |
| 268 | 271 | ||
| 269 | - | Single-part names resolve only to a matching local directory or `.zot` archive. Anyone can publish an agent as a public GitHub repository and run it as `owner/repository`, or publish a collection and run an agent directory as `owner/repository/agent`. zot has no built-in owner, official collection, configured registry, or allowlist. For GitHub sources, zot downloads the repository archive into a temporary directory, validates and runs the selected agent, then removes the downloaded source when the command exits. Agent data, consent receipts, and sessions still persist under `$ZOT_HOME`. See [docs/zotfiles.md](docs/zotfiles.md) for authoring, permissions, packaging, and current limitations. | |
| 272 | + | Single-part names resolve only to a matching local directory or `.zut` archive. Anyone can publish an agent as a public GitHub repository and run it as `owner/repository`, or publish a collection and run an agent directory as `owner/repository/agent`. zut has no built-in owner, official collection, configured registry, or allowlist. For GitHub sources, zut downloads the repository archive into a temporary directory, validates and runs the selected agent, then removes the downloaded source when the command exits. Agent data, consent receipts, and sessions still persist under `$ZUT_HOME`. See [docs/zutfiles.md](docs/zutfiles.md) for authoring, permissions, packaging, and current limitations. | |
| 270 | 273 | ||
| 271 | 274 | ## Embedding | |
| 272 | 275 | ||
| 273 | - | Two ways to drive zot from another program: | |
| 276 | + | Two ways to drive zut from another program: | |
| 274 | 277 | ||
| 275 | - | - **Go in-process**: import `github.com/patriceckhart/zot/packages/agent/sdk`. One `Runtime` per project; `Prompt(ctx, text, images)` returns a channel of `Event`. Small example in `examples/sdk/`. | |
| 276 | - | - **Any language, out-of-process**: spawn `zot rpc` as a subprocess and exchange newline-delimited JSON over its stdin/stdout. Wire format and event schema in [docs/rpc.md](docs/rpc.md). Reference clients live under `examples/rpc/`. | |
| 278 | + | - **Go in-process**: import `github.com/bnema/zut/packages/agent/sdk`. One `Runtime` per project; `Prompt(ctx, text, images)` returns a channel of `Event`. Small example in `examples/sdk/`. | |
| 279 | + | - **Any language, out-of-process**: spawn `zut rpc` as a subprocess and exchange newline-delimited JSON over its stdin/stdout. Wire format and event schema in [docs/rpc.md](docs/rpc.md). Reference clients live under `examples/rpc/`. | |
| 277 | 280 | ||
| 278 | 281 | Both interfaces share the same event schema, so transcripts captured by one can be replayed through the other. | |
| 279 | 282 | ||
| @@ -285,26 +288,26 @@ Slash command names are case-insensitive in the TUI and messaging backends; argu | |||
| 285 | 288 | |---|---| | |
| 286 | 289 | | `/help` | Show key bindings and commands. | | |
| 287 | 290 | | `/login` | Log in via API key or subscription (opens a dialog). | | |
| 288 | - | | `/logout [provider]` | Clear credentials for any logged-in provider, or all when omitted. `/logout openai-codex` clears ChatGPT/Codex subscription auth while preserving a public OpenAI API key; `/logout kimi` also disables fallback to the official Kimi Code CLI token until you log in to Kimi through zot again. | | |
| 291 | + | | `/logout [provider]` | Clear credentials for any logged-in provider, or all when omitted. `/logout openai-codex` clears ChatGPT/Codex subscription auth while preserving a public OpenAI API key; `/logout kimi` also disables fallback to the official Kimi Code CLI token until you log in to Kimi through zut again. | | |
| 289 | 292 | | `/model` | Pick a model from a list (or `/model <id>` to set directly). | | |
| 290 | 293 | | `/reasoning` | Set the reasoning level for subsequent model calls. | | |
| 291 | 294 | | `/llama` | Connect to the configured llama.cpp router, load, unload, or remove cached models, and search/download GGUF models from Hugging Face with live progress. Shown after llama.cpp login is configured. | | |
| 292 | 295 | | `/sessions` | Resume a previous session for this directory. The picker omits branches created only for tree navigation. | | |
| 293 | 296 | | `/fork` | Pick a previous user message and fork the current session after that turn. The selected turn becomes branch context and no provider turn starts during the checkout. | | |
| 294 | - | | `/session` | Four ops on the current session: `export` to a portable `.zotsession` file, `import` one back in, `fork` from a past user message into a new branch, `tree` to switch between branches. Opens a picker without an argument; direct forms: `/session export [path]`, `/session import <path>`, `/session fork`, `/session tree`. Default export destination is `~/Downloads`. `Esc` `Esc` from an idle, empty editor is a shortcut for `/session tree` when the terminal supplies two parsed bare Escape events. | | |
| 297 | + | | `/session` | Four ops on the current session: `export` to a portable `.zutsession` file, `import` one back in, `fork` from a past user message into a new branch, `tree` to switch between branches. Opens a picker without an argument; direct forms: `/session export [path]`, `/session import <path>`, `/session fork`, `/session tree`. Default export destination is `~/Downloads`. `Esc` `Esc` from an idle, empty editor is a shortcut for `/session tree` when the terminal supplies two parsed bare Escape events. | | |
| 295 | 298 | | `/jump` | Scroll the chat to a previous turn (or `/jump <text>` to filter). | | |
| 296 | 299 | | `/btw` | Side chat with full context that doesn't add to the main thread. | | |
| 297 | 300 | | `/subagents` | Spawn, monitor, and chat with background subagents. Each is a separate long-lived process with bounded lifecycle state, structured results, and optional Git worktree isolation. Named profiles can be selected with `agent`; see [docs/subagents.md](docs/subagents.md). | | |
| 298 | 301 | | `/skills` | List discovered skills (SKILL.md files) and preview their bodies. | | |
| 299 | 302 | | `/compact` | Summarize the transcript into one message to free up context. | | |
| 300 | - | | `/study` | Run the canned prompt "Read and understand everything in the current directory." so the agent has full project context before you start asking targeted questions. Pass a path — typed, drag-dropped, or selected via `@` — to target a specific file or directory instead: `/study [dir:packages/]`, `/study cmd/zot/main.go`. | | |
| 303 | + | | `/study` | Run the canned prompt "Read and understand everything in the current directory." so the agent has full project context before you start asking targeted questions. Pass a path — typed, drag-dropped, or selected via `@` — to target a specific file or directory instead: `/study [dir:packages/]`, `/study cmd/zut/main.go`. | | |
| 301 | 304 | | `/jail` | Confine tools to the current directory. | | |
| 302 | 305 | | `/unjail` | Allow tools to touch paths outside again. | | |
| 303 | 306 | | `/reload-ext` | Hot-reload all extensions (re-read manifests, respawn subprocesses, rebuild tool registry). | | |
| 304 | 307 | | `/telegram` | Connect, disconnect, or show status of the Telegram bridge (takes `connect` / `disconnect` / `status` as an optional argument; opens a picker without one). When connected, DMs from the paired user become prompts in the running session and the assistant's replies are mirrored back to Telegram. Alias: `/tg`. | | |
| 305 | - | | `/settings` | Change persistent settings, including inline images, terminal alerts, AI terminal titles, auto-subagents, Ponytail coding mode, fast mode, main/sub-agent LSP access, and the auto-compact threshold. Saved to `$ZOT_HOME/config.json`; setting changes apply without a restart, while AI title generation waits for the first real prompt. | | |
| 308 | + | | `/settings` | Change persistent settings, including inline images, terminal alerts, AI terminal titles, auto-subagents, Ponytail coding mode, fast mode, main/sub-agent LSP access, and the auto-compact threshold. Saved to `$ZUT_HOME/config.json`; setting changes apply without a restart, while AI title generation waits for the first real prompt. | | |
| 306 | 309 | | `/clear` | Clear the chat transcript. | | |
| 307 | - | | `/exit` | Exit zot. | | |
| 310 | + | | `/exit` | Exit zut. | | |
| 308 | 311 | ||
| 309 | 312 | Extension-registered commands appear under a divider at the bottom of the popup, sorted by name. | |
| 310 | 313 | ||
| @@ -314,17 +317,17 @@ Type `!` followed by a command to run it directly without going through the mode | |||
| 314 | 317 | ||
| 315 | 318 | ### `/sessions` | |
| 316 | 319 | ||
| 317 | - | Shows previous sessions for the current working directory, newest first, with timestamp, model, message count, cost, and the session title. Session entries load in the background and are revealed incrementally in newest-first order; an older result may wait briefly for entries ahead of it so the list never reorders, while the picker keeps showing progress for the remaining entries. Once an entry appears, `up`/`down` and `enter` can select it immediately; `esc` cancels and closes the picker. zot swaps the current session file for the selected one and replays the full transcript (including tool calls) into the agent. Sessions remember their stored provider and model: resuming rebuilds the live agent when necessary instead of silently using a different model; if that rebuild fails, the current session is left unchanged. Branches created for tree navigation remain available to `/session tree` but are hidden from this flat picker. A fresh interactive session asks the active model for a concise title after the first real prompt and restores that title when resumed. | |
| 320 | + | Shows previous sessions for the current working directory, newest first, with timestamp, model, message count, cost, and the session title. Session entries load in the background and are revealed incrementally in newest-first order; an older result may wait briefly for entries ahead of it so the list never reorders, while the picker keeps showing progress for the remaining entries. Once an entry appears, `up`/`down` and `enter` can select it immediately; `esc` cancels and closes the picker. zut swaps the current session file for the selected one and replays the full transcript (including tool calls) into the agent. Sessions remember their stored provider and model: resuming rebuilds the live agent when necessary instead of silently using a different model; if that rebuild fails, the current session is left unchanged. Branches created for tree navigation remain available to `/session tree` but are hidden from this flat picker. A fresh interactive session asks the active model for a concise title after the first real prompt and restores that title when resumed. | |
| 318 | 321 | ||
| 319 | 322 | ### `/session` | |
| 320 | 323 | ||
| 321 | 324 | Four ops on the current session. `/session` alone opens a picker; each is also runnable directly. | |
| 322 | 325 | ||
| 323 | - | - **`/session export [path]`**. Writes the running transcript to a portable `.zotsession` file. Default destination is `~/Downloads/<timestamp>-<session-id>-<prompt-slug>.zotsession`. Pass a path to override; a directory is fine (a dated name is built inside), a bare name gets `.zotsession` appended. The meta's cwd is stripped on the way out so the recipient doesn't see your filesystem layout. | |
| 326 | + | - **`/session export [path]`**. Writes the running transcript to a portable `.zutsession` file. Default destination is `~/Downloads/<timestamp>-<session-id>-<prompt-slug>.zutsession`. Pass a path to override; a directory is fine (a dated name is built inside), a bare name gets `.zutsession` appended. The meta's cwd is stripped on the way out so the recipient doesn't see your filesystem layout. | |
| 324 | 327 | ||
| 325 | - | **What's included.** Only the main chat thread of the running session — messages, tool calls, tool results, compactions, and usage. **`/subagents` subagents are NOT included.** Their transcripts, unix-socket inboxes, and per-agent session files are all machine-local; a `.zotsession` is just a chat transcript and has no way to revive a unix socket on another box. If you want the conversation, copy it out of the dashboard manually. | |
| 326 | - | - **`/session import <path>`**. Copies a `.zotsession` file into `$ZOT_HOME/sessions/<cwd-hash>/` with a fresh id and the current cwd, then switches the running agent onto it. Imported sessions are first-class: they show up in `/sessions` and `/jump`, and in the current-family tree when they are part of that family. Drag-drop paths in the editor are accepted (zot strips the surrounding quotes automatically). | |
| 327 | - | - **`/fork`** (also available as **`/session fork`**). Opens a turn picker (same shape as `/jump`). Pick any past user message; zot copies the transcript through that turn into a new branch and switches to it without starting a provider request. The parent session stays on disk. Use it to try a different question without polluting the original transcript, or to rewind after the agent went down the wrong path. Use a user-message row in `/session tree` when you want the prompt restored for editing, including image attachments. | |
| 328 | + | **What's included.** Only the main chat thread of the running session — messages, tool calls, tool results, compactions, and usage. **`/subagents` subagents are NOT included.** Their transcripts, unix-socket inboxes, and per-agent session files are all machine-local; a `.zutsession` is just a chat transcript and has no way to revive a unix socket on another box. If you want the conversation, copy it out of the dashboard manually. | |
| 329 | + | - **`/session import <path>`**. Copies a `.zutsession` file into `$ZUT_HOME/sessions/<cwd-hash>/` with a fresh id and the current cwd, then switches the running agent onto it. Imported sessions are first-class: they show up in `/sessions` and `/jump`, and in the current-family tree when they are part of that family. Drag-drop paths in the editor are accepted (zut strips the surrounding quotes automatically). | |
| 330 | + | - **`/fork`** (also available as **`/session fork`**). Opens a turn picker (same shape as `/jump`). Pick any past user message; zut copies the transcript through that turn into a new branch and switches to it without starting a provider request. The parent session stays on disk. Use it to try a different question without polluting the original transcript, or to rewind after the agent went down the wrong path. Use a user-message row in `/session tree` when you want the prompt restored for editing, including image attachments. | |
| 328 | 331 | - **`/session tree`**. Shows the current session's family: its root ancestor and descendants, not unrelated root sessions in the same cwd. Use `up`/`down` and `enter` to choose a row; `esc` closes the tree. The picker keeps pre-compaction history available as forkable user and assistant messages, while hiding tool calls, tool results, compaction checkpoints, shell/image helper rows, and other internal context. Message rows are indented at their branch points and the current endpoint is tagged `[current]`. Empty and detached branch points are represented by visible boundary rows. A child whose historical fork point no longer matches after compaction stays discoverable as a detached branch under the parent's current tail rather than disappearing. Parentless sessions are roots; orphaned children (whose parent file was deleted) remain roots. | |
| 329 | 332 | ||
| 330 | 333 | Selecting a user-message row creates a navigation branch before that message and restores the complete editable draft, including text blocks and image attachments; no provider request is made until you submit it. Selecting an assistant-message row checks out a safe message boundary, and selecting an empty or detached boundary checks out that branch point with an empty editor. Navigation branches are hidden from `/sessions` but remain in the tree. | |
| @@ -333,7 +336,7 @@ Four ops on the current session. `/session` alone opens a picker; each is also r | |||
| 333 | 336 | ||
| 334 | 337 | ### `/jump` | |
| 335 | 338 | ||
| 336 | - | Opens a turn picker for the current session, one row per user prompt, each showing the turn number, how many tools that turn invoked, and the first line of the prompt. `up`/`down` to pick, `enter` to jump, `esc` to cancel. Any printable rune while the picker is open extends a filter; backspace narrows it back. `/jump <text>` pre-applies the filter; if exactly one turn matches, zot jumps straight there without showing the picker. | |
| 339 | + | Opens a turn picker for the current session, one row per user prompt, each showing the turn number, how many tools that turn invoked, and the first line of the prompt. `up`/`down` to pick, `enter` to jump, `esc` to cancel. Any printable rune while the picker is open extends a filter; backspace narrows it back. `/jump <text>` pre-applies the filter; if exactly one turn matches, zut jumps straight there without showing the picker. | |
| 337 | 340 | ||
| 338 | 341 | Jumping is non-destructive. The transcript is untouched, the viewport just scrolls so the chosen turn is at the top. A muted line at the top of the chat reads `viewing turn N of M, pgdn to catch up`. Scroll back to the bottom with `pgdn` (or keep scrolling with the arrow keys) and the indicator goes away. | |
| 339 | 342 | ||
| @@ -352,7 +355,7 @@ Inside the overlay: `enter` sends, `esc` cancels an in-flight call (or closes th | |||
| 352 | 355 | ||
| 353 | 356 | ### `/subagents` | |
| 354 | 357 | ||
| 355 | - | Background subagents that run alongside your main session. Each one is a separate `zot` subprocess with its own model loop, persistent session file, versioned JSONL control protocol, structured turn result, and lifecycle state. Shared checkout mode preserves the historical behavior; worktree mode creates a temporary detached Git worktree, captures a patch and changed-file list, and never merges automatically. | |
| 358 | + | Background subagents that run alongside your main session. Each one is a separate `zut` subprocess with its own model loop, persistent session file, versioned JSONL control protocol, structured turn result, and lifecycle state. Shared checkout mode preserves the historical behavior; worktree mode creates a temporary detached Git worktree, captures a patch and changed-file list, and never merges automatically. | |
| 356 | 359 | ||
| 357 | 360 | > **Choose the workspace deliberately.** Use `isolation:"worktree"` for parallel coding so children cannot edit the host checkout. Use shared mode for read-only review or explicitly coordinated work. Worktree isolation is an accident-prevention guardrail, not a security sandbox. | |
| 358 | 361 | ||
| @@ -389,37 +392,37 @@ Background subagents that run alongside your main session. Each one is a separat | |||
| 389 | 392 | ||
| 390 | 393 | **Session scoping** — each agent is stamped with the host session that spawned it and only shows up in that session's dashboard. Swap sessions with `/sessions` and the dashboard re-narrows accordingly. Agents from other sessions keep running in the background and reappear when you switch back. | |
| 391 | 394 | ||
| 392 | - | **Persistence across zot restarts** — every spawn writes a durable manifest, append-only event log, session file, and structured `result.json` under `$ZOT_HOME/subagents/agents/<id>/`. On the next `zot` launch they show up as **detached**; use `R` or `/subagents resume-session <id>` to continue the existing session. `/subagents restart-task <id>` is the explicit operation that intentionally replays the stored task. | |
| 395 | + | **Persistence across zut restarts** — every spawn writes a durable manifest, append-only event log, session file, and structured `result.json` under `$ZUT_HOME/subagents/agents/<id>/`. On the next `zut` launch they show up as **detached**; use `R` or `/subagents resume-session <id>` to continue the existing session. `/subagents restart-task <id>` is the explicit operation that intentionally replays the stored task. | |
| 393 | 396 | ||
| 394 | - | **Where state lives** — per-agent manifests, session files, events, results, and patches live under `$ZOT_HOME/subagents/agents/<id>/`; inbox sockets are runtime-only and permission-restricted. Shared-mode edits land directly in the repo. Worktree-mode edits are captured as durable patches and changed-file summaries before cleanup. | |
| 397 | + | **Where state lives** — per-agent manifests, session files, events, results, and patches live under `$ZUT_HOME/subagents/agents/<id>/`; inbox sockets are runtime-only and permission-restricted. Shared-mode edits land directly in the repo. Worktree-mode edits are captured as durable patches and changed-file summaries before cleanup. | |
| 395 | 398 | ||
| 396 | - | **`/session export` does NOT bundle subagents.** A `.zotsession` is just the main chat transcript; per-agent state (session file, unix-socket inbox) is machine-local and doesn't round-trip through a JSONL file. To share what an agent said, copy it out of the transcript view manually. | |
| 399 | + | **`/session export` does NOT bundle subagents.** A `.zutsession` is just the main chat transcript; per-agent state (session file, unix-socket inbox) is machine-local and doesn't round-trip through a JSONL file. To share what an agent said, copy it out of the transcript view manually. | |
| 397 | 400 | ||
| 398 | 401 | **Auto-subagents.** With `/settings` -> auto-subagents on, the main agent gets the canonical `subagent_spawn` tool and a system-prompt nudge to delegate independent work. The companion `subagent_status` tool accepts an optional `agent_id`: omit it to list visible workers, or pass a full id / unique prefix to query one without waiting for completion. It returns privacy-safe JSON metadata with the worker id, lifecycle state (`starting`, `running`, `completed`, `failed`, `cancelled`, or `detached`), underlying process/turn state, timestamps, a bounded first-line task summary, and terminal-result metadata when available; it does not return prompts, transcripts, result output, credentials, provider settings, or filesystem paths. The spawn tool returns the child id and initial state immediately; completion is a durable `turn.result` with a `subagent://<id>/result` reference. It accepts named profiles, timeout, `max_turns`, and `isolation:"shared"` or `isolation:"worktree"`. Omit `max_turns` to use the supervisor policy; the default ceiling is 3 and `subagents.max_turns` changes it. Global concurrency and total-spawn budgets apply equally to slash commands, tool calls, and batches. Off by default; toggle from `/settings`. | |
| 399 | 402 | ||
| 400 | 403 | ### Named subagent profiles | |
| 401 | 404 | ||
| 402 | - | zot discovers the common markdown/frontmatter profile layout from `~/.agents/agents/*.md` by default. With auto-subagents enabled, the main agent receives `[subagents_list]` metadata and can select a named profile through `subagent_spawn`. Profile bodies are applied only to the selected child. See [docs/subagents.md](docs/subagents.md) for discovery, supported fields, reasoning levels, and examples. | |
| 405 | + | zut discovers the common markdown/frontmatter profile layout from `~/.agents/agents/*.md` by default. With auto-subagents enabled, the main agent receives `[subagents_list]` metadata and can select a named profile through `subagent_spawn`. Profile bodies are applied only to the selected child. See [docs/subagents.md](docs/subagents.md) for discovery, supported fields, reasoning levels, and examples. | |
| 403 | 406 | ||
| 404 | 407 | ### `/settings` | |
| 405 | 408 | ||
| 406 | - | Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` or `space` to change the selected row, `esc` to close (rows that open a sub-view, like model shortcuts, use `esc` to go back one level first). Changes are written to `$ZOT_HOME/config.json` without a restart; individual settings document whether they affect the current turn, the next model call, or a later session event. Current settings: | |
| 409 | + | Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` or `space` to change the selected row, `esc` to close (rows that open a sub-view, like model shortcuts, use `esc` to go back one level first). Changes are written to `$ZUT_HOME/config.json` without a restart; individual settings document whether they affect the current turn, the next model call, or a later session event. Current settings: | |
| 407 | 410 | ||
| 408 | 411 | - **render images when supported** — draw screenshots / `read`-returned images inline using the terminal's image protocol, or fall back to a text placeholder. Auto-detected from `TERM_PROGRAM`; the toggle overrides the detection. The row is greyed out and forced off on terminals that don't speak any image protocol. | |
| 409 | 412 | - **terminal alerts** — emit a terminal bell when the main session stops with work that may need attention, or when an extension raises a structured alert. Enabled by default; changes apply immediately and persist as `terminal_alerts_enabled`. Terminal emulators may render the bell audibly, visually, or not at all. | |
| 410 | - | - **AI terminal titles** — after the first real prompt of a fresh interactive session, make one small hidden request to the active model and set the terminal title to `zot: <title>`. The title is limited to 40 Unicode characters, persisted with the session, restored on resume, and never added to the conversation. Enabled by default; disable it to avoid the extra model request. The toggle applies immediately, but title generation still waits for the first real prompt and never starts from startup context, resumed history, or slash commands. Persists as `terminal_title_enabled`. | |
| 413 | + | - **AI terminal titles** — after the first real prompt of a fresh interactive session, make one small hidden request to the active model and set the terminal title to `zut: <title>`. The title is limited to 40 Unicode characters, persisted with the session, restored on resume, and never added to the conversation. Enabled by default; disable it to avoid the extra model request. The toggle applies immediately, but title generation still waits for the first real prompt and never starts from startup context, resumed history, or slash commands. Persists as `terminal_title_enabled`. | |
| 411 | 414 | - **auto-subagents** — let the main agent spawn background sub-agents in parallel via `subagent_spawn` and query live state via `subagent_status`. Off by default. The tools accept named profiles, reasoning, timeout, turn limits, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion updates still arrive through the auto-subagents watcher. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details. | |
| 412 | - | - **Ponytail coding mode** — include compact engineering guidance in each resolved system prompt; the guidance tells the model to apply it to coding, debugging, and review work rather than ordinary conversation. It favors understanding the real flow, small validated changes, reuse, and preserving safety checks. Enabled by default; changes apply to the next model call and persist as `ponytail_enabled` in `$ZOT_HOME/config.json`. It is included by interactive, print, stream, JSON, RPC, subagent, bot, SDK, and Zotfile agents. | |
| 415 | + | - **Ponytail coding mode** — include compact engineering guidance in each resolved system prompt; the guidance tells the model to apply it to coding, debugging, and review work rather than ordinary conversation. It favors understanding the real flow, small validated changes, reuse, and preserving safety checks. Enabled by default; changes apply to the next model call and persist as `ponytail_enabled` in `$ZUT_HOME/config.json`. It is included by interactive, print, stream, JSON, RPC, subagent, bot, SDK, and Zutfile agents. | |
| 413 | 416 | - **fast mode** — request OpenAI's Fast service tier for OpenAI, OpenAI Responses, and OpenAI Codex models. Off by default; changes apply on the next model call and persist as `fast_mode`. Other providers return an unsupported-provider error. Fast mode may cost more and depends on the selected OpenAI model/account. | |
| 414 | 417 | - **lsp in main session** — enable the built-in `lsp` tool and code diagnostics for the main agent. Enabled by default; changes persist as `lsp_enabled`. | |
| 415 | 418 | - **lsp in sub-agents** — allow newly spawned background sub-agents to use the built-in `lsp` tool. Enabled by default; changes persist as `subagent_lsp_enabled` and apply when a child starts. | |
| 416 | - | - **auto-compact threshold** — choose `off`, `70%`, `80%`, `85%` (default), or `90%` of the model's advertised context window. The selected percentage controls automatic compaction before and after interactive turns and persists as `auto_compact_threshold`. After a successful threshold compaction, zot automatically resumes an unfinished most-recent intent; a prompt queued during compaction takes priority. `off` disables percentage-based triggers but keeps manual `/compact` and automatic recovery from context-window and payload-too-large responses. | |
| 419 | + | - **auto-compact threshold** — choose `off`, `70%`, `80%`, `85%` (default), or `90%` of the model's advertised context window. The selected percentage controls automatic compaction before and after interactive turns and persists as `auto_compact_threshold`. After a successful threshold compaction, zut automatically resumes an unfinished most-recent intent; a prompt queued during compaction takes priority. `off` disables percentage-based triggers but keeps manual `/compact` and automatic recovery from context-window and payload-too-large responses. | |
| 417 | 420 | - **jail new sessions by default**: start every new agent with tools confined to its working directory. Off by default. The setting applies to interactive, print, JSON, RPC, and background-agent runs, persists as `jail_by_default`, and immediately updates the current interactive session. `/jail` and `/unjail` remain session-scoped overrides and do not change this default. | |
| 418 | 421 | - **compact transcript rendering**: reduce visual chrome in the chat transcript. Tool calls render as a quiet header plus indented output instead of a bordered box, and sent messages render without padded background bubbles. Off by default. Changes apply immediately and persist to `config.json` as `compact_mode`. | |
| 419 | - | - **show loaded resources at startup**: list the active Zotfile agent (for `zot run`), loaded `AGENTS.md` paths, extensions, and user-installed skills in compact sections above the transcript. Built-in skills are omitted. Off by default. Changes apply immediately and persist to `config.json` as `show_instructions_at_startup`. | |
| 422 | + | - **show loaded resources at startup**: list the active Zutfile agent (for `zut run`), loaded `AGENTS.md` paths, extensions, and user-installed skills in compact sections above the transcript. Built-in skills are omitted. Off by default. Changes apply immediately and persist to `config.json` as `show_instructions_at_startup`. | |
| 420 | 423 | - **TUI settings**: opens a sub-view for input layout and status placement. **Input style** can be `plain` (default prompt line), `lines` (separator lines above and below the input), or `block` (a user-bubble-style input block). **Status position** places model, usage, and working-directory information above or below the input. **Working spinner position** places the busy spinner above or below the input. Changes apply immediately and persist to `config.json` as `tui_input_style`, `tui_status_position`, and `tui_working_position` (`above_input` or `below_input` for the position fields). | |
| 421 | 424 | - **reasoning level**: choose reasoning for supported models: off, minimum, low, medium, high, xhigh, or max. The `max` tier is opt-in and sent natively to GPT-5.6 and adaptive-thinking Claude models; unsupported backends clamp it to their highest accepted effort. The change is persisted to `config.json` and applied to the next model call. Use `/reasoning` to open this selector directly. The selector only shows distinct levels supported by the active model; models without reasoning support only offer `off`. | |
| 422 | - | - **color theme** — choose the built-in auto, inherited (from terminal), dark, or light theme, or any JSON theme discovered under `$ZOT_HOME/themes` or a loaded extension. Built-in and JSON themes follow terminal color capability: truecolor terminals receive RGB output, while RGB values fall back to xterm-256 elsewhere. Theme files can use xterm-256, ANSI, or RGB values for semantic UI colors and can also override syntax colors and spinner frames/messages. Changes apply immediately; if a selected theme file is deleted, zot resets to auto. See [docs/themes.md](docs/themes.md). | |
| 425 | + | - **color theme** — choose the built-in auto, inherited (from terminal), dark, or light theme, or any JSON theme discovered under `$ZUT_HOME/themes` or a loaded extension. Built-in and JSON themes follow terminal color capability: truecolor terminals receive RGB output, while RGB values fall back to xterm-256 elsewhere. Theme files can use xterm-256, ANSI, or RGB values for semantic UI colors and can also override syntax colors and spinner frames/messages. Changes apply immediately; if a selected theme file is deleted, zut resets to auto. See [docs/themes.md](docs/themes.md). | |
| 423 | 426 | - **model shortcuts** — opens a sub-view with nine slots (`model 1` ... `model 9`). `enter` on a slot opens the same `/model` selector and binds the chosen provider/model to that slot; `backspace` clears a slot. Once assigned, press `Ctrl+1` ... `Ctrl+9` from the editor to switch the active model instantly (the same cross-provider swap `/model` performs, transcript and cost carried over). Assigning a shortcut does not change the current model. Shortcuts are skipped while a turn is running. | |
| 424 | 427 | ||
| 425 | 428 | ### `/skills` | |
| @@ -430,21 +433,21 @@ Opens a picker listing every discovered SKILL.md file, built-ins hidden. Each ro | |||
| 430 | 433 | ||
| 431 | 434 | Sends the current transcript through the model with a structured summarization prompt. The returned summary replaces the transcript as one synthetic user message, with the last few exchanges kept verbatim for continuity. The status bar's context meter resets. Use it when the context meter creeps past ~80%. | |
| 432 | 435 | ||
| 433 | - | zot also auto-compacts in the background: after any turn that reaches the configured context threshold, the agent kicks off a condense pass on its own and, once it succeeds, resumes an unfinished most-recent intent automatically. Completed text answers settle normally, while a prompt queued during compaction runs instead of the automatic continuation. Choose `off`, `70%`, `80%`, `85%` (default), or `90%` under `/settings` → **auto-compact threshold**. You'll see `condensing history, esc to cancel` above the status bar and an `(auto)` tag next to the context percentage; `esc` aborts it without touching the transcript. Turning the percentage trigger off does not disable automatic compaction and retry after a context-window or payload-too-large response. | |
| 436 | + | zut also auto-compacts in the background: after any turn that reaches the configured context threshold, the agent kicks off a condense pass on its own and, once it succeeds, resumes an unfinished most-recent intent automatically. Completed text answers settle normally, while a prompt queued during compaction runs instead of the automatic continuation. Choose `off`, `70%`, `80%`, `85%` (default), or `90%` under `/settings` → **auto-compact threshold**. You'll see `condensing history, esc to cancel` above the status bar and an `(auto)` tag next to the context percentage; `esc` aborts it without touching the transcript. Turning the percentage trigger off does not disable automatic compaction and retry after a context-window or payload-too-large response. | |
| 434 | 437 | ||
| 435 | 438 | ### `/jail` | |
| 436 | 439 | ||
| 437 | 440 | Enforces a sandbox rooted at the cwd shown in the status bar. `read`, `write`, and `edit` resolve their target path (including through symlinks) and refuse anything outside the sandbox. `bash` refuses obvious escape patterns (`sudo`, `rm -rf /`, leading `cd /`, `cd ..`, `cd ~`, `chmod -R`, `dd of=/`, and similar) and rejects shell arguments or redirections that point outside the sandbox. The status bar shows `jailed, ~/your/cwd` while active. Enable **jail new sessions by default** in `/settings` to persist this behavior across launches; `/unjail` then unlocks only the current session. | |
| 438 | 441 | ||
| 439 | - | This is a guardrail against accidents, not a hard security boundary. If you need real isolation, run zot under docker or a proper sandbox. | |
| 442 | + | This is a guardrail against accidents, not a hard security boundary. If you need real isolation, run zut under docker or a proper sandbox. | |
| 440 | 443 | ||
| 441 | 444 | ## Sessions | |
| 442 | 445 | ||
| 443 | - | Every interactive or print/json run (unless `--no-session`) writes a JSONL transcript under `$ZOT_HOME/sessions/<cwd-hash>/`. Resume any of them with `--continue`, `--resume`, `--session <path>`, or interactively via `/sessions` inside the TUI. Session metadata stores the provider and model used by that session; resume honors that pair, rebuilding the active agent when it differs and refusing the switch without discarding the current session if rebuilding fails. Empty sessions (the user exited without prompting) are deleted on close so the list stays tidy. | |
| 446 | + | Every interactive or print/json run (unless `--no-session`) writes a JSONL transcript under `$ZUT_HOME/sessions/<cwd-hash>/`. Resume any of them with `--continue`, `--resume`, `--session <path>`, or interactively via `/sessions` inside the TUI. Session metadata stores the provider and model used by that session; resume honors that pair, rebuilding the active agent when it differs and refusing the switch without discarding the current session if rebuilding fails. Empty sessions (the user exited without prompting) are deleted on close so the list stays tidy. | |
| 444 | 447 | ||
| 445 | 448 | ## Providers | |
| 446 | 449 | ||
| 447 | - | zot's built-in provider catalog includes: | |
| 450 | + | zut's built-in provider catalog includes: | |
| 448 | 451 | ||
| 449 | 452 | - **Subscription-capable**: Anthropic Claude Pro/Max (`anthropic`), OpenAI Codex / ChatGPT Plus/Pro (`openai-codex`), Kimi Code (`kimi`), SuperGrok/X Premium (`xai`), GitHub Copilot (`github-copilot`). | |
| 450 | 453 | - **Direct API providers**: Anthropic, OpenAI Chat Completions, OpenAI Responses, DeepSeek, Google Gemini, Kimi/Moonshot, Moonshot CN, Groq, Cerebras, xAI, Together AI, Hugging Face Router, OpenRouter, Mistral, Z.AI, Xiaomi/MiMo token-plan regions, MiniMax global/CN, Fireworks, Vercel AI Gateway, OpenCode/OpenCode Go. | |
| @@ -457,15 +460,15 @@ Use `/login` to store API keys or subscription credentials. `/model` only shows | |||
| 457 | 460 | ||
| 458 | 461 | `--list-models` or the `/model` picker shows the full catalog across all built-in providers. Three sources: | |
| 459 | 462 | ||
| 460 | - | - **Catalog**: models baked into zot, covering Claude, GPT/Codex, Gemini/Gemma, Kimi/Moonshot, DeepSeek, Groq-hosted Llama/Gemma/Compound, OpenRouter-routed models, Bedrock model ids, Vertex model ids, Azure OpenAI deployments, Copilot models, and other provider-specific catalog entries. | |
| 461 | - | - **Live**: IDs discovered from `GET /v1/models` using your stored API key (cached for 6h in `$ZOT_HOME/models-cache.json`, refreshed in the background on startup). | |
| 463 | + | - **Catalog**: models baked into zut, covering Claude, GPT/Codex, Gemini/Gemma, Kimi/Moonshot, DeepSeek, Groq-hosted Llama/Gemma/Compound, OpenRouter-routed models, Bedrock model ids, Vertex model ids, Azure OpenAI deployments, Copilot models, and other provider-specific catalog entries. | |
| 464 | + | - **Live**: IDs discovered from `GET /v1/models` using your stored API key (cached for 6h in `$ZUT_HOME/models-cache.json`, refreshed in the background on startup). | |
| 462 | 465 | - **Speculative**: IDs that appear in the upstream generator but aren't live on the public API yet. They'll 404 today and start working the moment the provider ships them. | |
| 463 | 466 | ||
| 464 | 467 | The context meter in the status line uses the model's advertised context window to show how much of it your last turn consumed. | |
| 465 | 468 | ||
| 466 | 469 | ### Model fallback (rescue) | |
| 467 | 470 | ||
| 468 | - | When a turn fails because of a recoverable provider error — expired token (`401`), permission denied (`403`), rate limit (`429`), provider outage (`502`/`503`/`504`), or a transient network failure — zot opens a **rescue** picker over the chat instead of just painting a red banner. | |
| 471 | + | When a turn fails because of a recoverable provider error — expired token (`401`), permission denied (`403`), rate limit (`429`), provider outage (`502`/`503`/`504`), or a transient network failure — zut opens a **rescue** picker over the chat instead of just painting a red banner. | |
| 469 | 472 | ||
| 470 | 473 | The picker is the same vertical list / fuzzy filter UI as `/model`, but it only shows models from providers you're currently logged in to (env vars, `auth.json`, Kimi CLI fallback, ollama). The failed model is excluded. Press `↑`/`↓` to choose, `enter` to retry the **same prompt** on the new model, `esc` to dismiss. | |
| 471 | 474 | ||
| @@ -477,7 +480,7 @@ No configuration is required — the candidate list is built dynamically from yo | |||
| 477 | 480 | ||
| 478 | 481 | ### Custom models | |
| 479 | 482 | ||
| 480 | - | Place a `models.json` in `$ZOT_HOME` (`$XDG_STATE_HOME/zot/` when set, otherwise the platform default above) to add models that aren't in the baked-in catalog or to override existing entries: | |
| 483 | + | Place a `models.json` in `$ZUT_HOME` (`$XDG_STATE_HOME/zut/` when set, otherwise the platform default above) to add models that aren't in the baked-in catalog or to override existing entries: | |
| 481 | 484 | ||
| 482 | 485 | ```json | |
| 483 | 486 | { | |
| @@ -526,7 +529,7 @@ A top-level provider key that is not a built-in id defines a custom provider. Gi | |||
| 526 | 529 | ||
| 527 | 530 | Custom providers are first-class: they appear in `--list-models`, `/model`, and `/login`. `models.json` never stores secrets. Supply the key through `/login`, `--api-key`, or a derived environment variable in upper snake case (so `my-company` reads `MY_COMPANY_API_KEY`). Because many self-hosted gateways do not expose a model-list endpoint, custom provider keys are accepted and stored without a verification probe; an invalid key surfaces on the first model call. | |
| 528 | 531 | ||
| 529 | - | To retrieve this custom provider's key from a password manager, add a matching entry to `$ZOT_HOME/auth.json`: | |
| 532 | + | To retrieve this custom provider's key from a password manager, add a matching entry to `$ZUT_HOME/auth.json`: | |
| 530 | 533 | ||
| 531 | 534 | ```json | |
| 532 | 535 | { | |
| @@ -545,15 +548,15 @@ To retrieve this custom provider's key from a password manager, add a matching e | |||
| 545 | 548 | The provider IDs in `models.json` and `auth.json` must match. Then select the custom model directly: | |
| 546 | 549 | ||
| 547 | 550 | ```bash | |
| 548 | - | zot --provider my-company --model company-llm-v2 | |
| 551 | + | zut --provider my-company --model company-llm-v2 | |
| 549 | 552 | ``` | |
| 550 | 553 | ||
| 551 | 554 | ### Kimi Code | |
| 552 | 555 | ||
| 553 | - | zot has built-in Kimi support through the Kimi Coding endpoint and Moonshot's OpenAI-compatible chat API. | |
| 556 | + | zut has built-in Kimi support through the Kimi Coding endpoint and Moonshot's OpenAI-compatible chat API. | |
| 554 | 557 | ||
| 555 | 558 | ```bash | |
| 556 | - | zot --provider kimi | |
| 559 | + | zut --provider kimi | |
| 557 | 560 | ``` | |
| 558 | 561 | ||
| 559 | 562 | By default this uses: | |
| @@ -566,15 +569,15 @@ Credential lookup order for Kimi: | |||
| 566 | 569 | 1. `--api-key` | |
| 567 | 570 | 2. `KIMI_API_KEY` | |
| 568 | 571 | 3. `MOONSHOT_API_KEY` | |
| 569 | - | 4. `$ZOT_HOME/auth.json` | |
| 572 | + | 4. `$ZUT_HOME/auth.json` | |
| 570 | 573 | 5. the official Kimi Code CLI token at `~/.kimi/credentials/kimi-code.json`, unless disabled by `/logout kimi` | |
| 571 | 574 | ||
| 572 | - | Use `/login` for either API-key login or Kimi Code subscription login. The subscription flow uses Kimi Code's device-code OAuth flow: zot opens the verification URL, waits for browser approval, stores the token in `auth.json`, and refreshes it automatically. | |
| 575 | + | Use `/login` for either API-key login or Kimi Code subscription login. The subscription flow uses Kimi Code's device-code OAuth flow: zut opens the verification URL, waits for browser approval, stores the token in `auth.json`, and refreshes it automatically. | |
| 573 | 576 | ||
| 574 | 577 | For direct Moonshot API keys or a custom compatible endpoint: | |
| 575 | 578 | ||
| 576 | 579 | ```bash | |
| 577 | - | zot --provider kimi --model kimi-k2-0905-preview --base-url https://api.moonshot.ai/v1 --api-key "$KIMI_API_KEY" | |
| 580 | + | zut --provider kimi --model kimi-k2-0905-preview --base-url https://api.moonshot.ai/v1 --api-key "$KIMI_API_KEY" | |
| 578 | 581 | ``` | |
| 579 | 582 | ||
| 580 | 583 | Kimi K3 is built in as `kimi/k3`, `moonshotai/kimi-k3`, `moonshotai-cn/kimi-k3`, `opencode-go/kimi-k3`, `openrouter/moonshotai/kimi-k3`, and `vercel-ai-gateway/moonshotai/kimi-k3`. Its output limit is 131,072 tokens on every built-in route. | |
| @@ -587,10 +590,10 @@ xAI supports either `XAI_API_KEY` or `/login` subscription authentication. The s | |||
| 587 | 590 | ||
| 588 | 591 | ### DeepSeek | |
| 589 | 592 | ||
| 590 | - | zot has built-in DeepSeek support through DeepSeek's OpenAI-compatible chat API. | |
| 593 | + | zut has built-in DeepSeek support through DeepSeek's OpenAI-compatible chat API. | |
| 591 | 594 | ||
| 592 | 595 | ```bash | |
| 593 | - | zot --provider deepseek | |
| 596 | + | zut --provider deepseek | |
| 594 | 597 | ``` | |
| 595 | 598 | ||
| 596 | 599 | By default this uses: | |
| @@ -604,26 +607,26 @@ Credential lookup order for DeepSeek: | |||
| 604 | 607 | ||
| 605 | 608 | 1. `--api-key` | |
| 606 | 609 | 2. `DEEPSEEK_API_KEY` | |
| 607 | - | 3. `$ZOT_HOME/auth.json` | |
| 610 | + | 3. `$ZUT_HOME/auth.json` | |
| 608 | 611 | ||
| 609 | - | Use `/login` and pick **api key** to paste a DeepSeek key. zot probes `/v1/models` once and stores the key under `deepseek` in `auth.json`. | |
| 612 | + | Use `/login` and pick **api key** to paste a DeepSeek key. zut probes `/v1/models` once and stores the key under `deepseek` in `auth.json`. | |
| 610 | 613 | ||
| 611 | 614 | > **Auth model: API key only.** DeepSeek does not offer a subscription OAuth flow. The `/login subscription` step lists only Anthropic, OpenAI, and Kimi; DeepSeek shows up only under `/login → api key`. | |
| 612 | 615 | ||
| 613 | - | > **Text only at the wire level.** DeepSeek's chat-completions endpoint currently rejects the multimodal content schema (`unknown variant image_url, expected text`). When the active provider is `deepseek`, zot silently drops `ImageBlock` parts from outgoing user/tool messages and keeps only the text. Switching back to a vision-capable model (Claude, GPT-4o/5, Gemini) re-sends the image normally because the session file still stores it. | |
| 616 | + | > **Text only at the wire level.** DeepSeek's chat-completions endpoint currently rejects the multimodal content schema (`unknown variant image_url, expected text`). When the active provider is `deepseek`, zut silently drops `ImageBlock` parts from outgoing user/tool messages and keeps only the text. Switching back to a vision-capable model (Claude, GPT-4o/5, Gemini) re-sends the image normally because the session file still stores it. | |
| 614 | 617 | ||
| 615 | 618 | For a custom-compatible endpoint (mirror, gateway, self-host): | |
| 616 | 619 | ||
| 617 | 620 | ```bash | |
| 618 | - | zot --provider deepseek --base-url https://my-deepseek-mirror.example.com/v1 --api-key "$DEEPSEEK_API_KEY" | |
| 621 | + | zut --provider deepseek --base-url https://my-deepseek-mirror.example.com/v1 --api-key "$DEEPSEEK_API_KEY" | |
| 619 | 622 | ``` | |
| 620 | 623 | ||
| 621 | 624 | ### Google Gemini | |
| 622 | 625 | ||
| 623 | - | zot has built-in Google Gemini support through the [AI Studio Generative Language API](https://aistudio.google.com/). | |
| 626 | + | zut has built-in Google Gemini support through the [AI Studio Generative Language API](https://aistudio.google.com/). | |
| 624 | 627 | ||
| 625 | 628 | ```bash | |
| 626 | - | zot --provider google | |
| 629 | + | zut --provider google | |
| 627 | 630 | ``` | |
| 628 | 631 | ||
| 629 | 632 | By default this uses: | |
| @@ -638,9 +641,9 @@ Credential lookup order for Google: | |||
| 638 | 641 | 1. `--api-key` | |
| 639 | 642 | 2. `GEMINI_API_KEY` | |
| 640 | 643 | 3. `GOOGLE_API_KEY` | |
| 641 | - | 4. `$ZOT_HOME/auth.json` | |
| 644 | + | 4. `$ZUT_HOME/auth.json` | |
| 642 | 645 | ||
| 643 | - | Use `/login` and pick **api key** to paste an AI Studio key. zot probes `/v1beta/models` once and stores the key under `google` in `auth.json`. | |
| 646 | + | Use `/login` and pick **api key** to paste an AI Studio key. zut probes `/v1beta/models` once and stores the key under `google` in `auth.json`. | |
| 644 | 647 | ||
| 645 | 648 | > **Auth model: API key only (this provider).** Google does not issue OAuth tokens for consumer Gemini Advanced / Google One AI Premium subscriptions, so there is no "log in with your Google subscription" flow. The `/login subscription` step quietly downgrades to the api-key form when you pick Google so you don't end up in a dead end. If you need OAuth/service-account auth instead of an API key, use the `google-vertex` provider below. | |
| 646 | 649 | ||
| @@ -652,10 +655,10 @@ You can add additional Gemini model IDs to `models.json` under the `google` prov | |||
| 652 | 655 | ||
| 653 | 656 | ### Gemini Enterprise Agent Platform (formerly Google Vertex AI) | |
| 654 | 657 | ||
| 655 | - | zot also has built-in support for [Gemini Enterprise Agent Platform](https://cloud.google.com/products/gemini-enterprise-agent-platform), Google's enterprise/GCP-hosted Gemini endpoint. Unlike the AI Studio `google` provider above, the `google-vertex` provider supports a Google Cloud API key plus `service_account` and `authorized_user` credential files. It does not support consumer Google login or the complete ADC credential chain, such as metadata-server and workload-identity credentials. | |
| 658 | + | zut also has built-in support for [Gemini Enterprise Agent Platform](https://cloud.google.com/products/gemini-enterprise-agent-platform), Google's enterprise/GCP-hosted Gemini endpoint. Unlike the AI Studio `google` provider above, the `google-vertex` provider supports a Google Cloud API key plus `service_account` and `authorized_user` credential files. It does not support consumer Google login or the complete ADC credential chain, such as metadata-server and workload-identity credentials. | |
| 656 | 659 | ||
| 657 | 660 | ```bash | |
| 658 | - | zot --provider google-vertex | |
| 661 | + | zut --provider google-vertex | |
| 659 | 662 | ``` | |
| 660 | 663 | ||
| 661 | 664 | Required configuration (env vars, read at construction time): | |
| @@ -667,34 +670,34 @@ Credential lookup order for Vertex: | |||
| 667 | 670 | ||
| 668 | 671 | 1. `GOOGLE_CLOUD_API_KEY`: simplest option. An API key created in the GCP console is sent as `x-goog-api-key`, without a token exchange. | |
| 669 | 672 | 2. `GOOGLE_APPLICATION_CREDENTIALS`: path to a supported credential JSON file. | |
| 670 | - | 3. **Default ADC file**: if neither of the above is set, zot checks the file written by `gcloud auth application-default login`. This is `~/.config/gcloud/application_default_credentials.json` on Unix systems and `%APPDATA%\gcloud\application_default_credentials.json` on Windows. | |
| 673 | + | 3. **Default ADC file**: if neither of the above is set, zut checks the file written by `gcloud auth application-default login`. This is `~/.config/gcloud/application_default_credentials.json` on Unix systems and `%APPDATA%\gcloud\application_default_credentials.json` on Windows. | |
| 671 | 674 | ||
| 672 | 675 | Two credential file shapes are supported: | |
| 673 | 676 | ||
| 674 | - | - `type: "service_account"`: zot signs a JWT with the private key and exchanges it for a short-lived access token. | |
| 675 | - | - `type: "authorized_user"`: zot exchanges the stored client ID, client secret, and refresh token for a short-lived access token. | |
| 677 | + | - `type: "service_account"`: zut signs a JWT with the private key and exchanges it for a short-lived access token. | |
| 678 | + | - `type: "authorized_user"`: zut exchanges the stored client ID, client secret, and refresh token for a short-lived access token. | |
| 676 | 679 | ||
| 677 | 680 | Access tokens are cached in memory and refreshed on demand. | |
| 678 | 681 | ||
| 679 | - | If none of these are available, zot errors with `vertex: no auth — set GOOGLE_CLOUD_API_KEY or GOOGLE_APPLICATION_CREDENTIALS`. | |
| 682 | + | If none of these are available, zut errors with `vertex: no auth — set GOOGLE_CLOUD_API_KEY or GOOGLE_APPLICATION_CREDENTIALS`. | |
| 680 | 683 | ||
| 681 | 684 | ### Local models with ollama | |
| 682 | 685 | ||
| 683 | - | zot works with [ollama](https://ollama.com) out of the box. Ollama serves an OpenAI-compatible API locally, so any model you have pulled works with zot. | |
| 686 | + | zut works with [ollama](https://ollama.com) out of the box. Ollama serves an OpenAI-compatible API locally, so any model you have pulled works with zut. | |
| 684 | 687 | ||
| 685 | 688 | Quick start: | |
| 686 | 689 | ||
| 687 | 690 | ```bash | |
| 688 | 691 | ollama pull qwen3.5:4b | |
| 689 | - | zot --provider ollama --model qwen3.5:4b | |
| 692 | + | zut --provider ollama --model qwen3.5:4b | |
| 690 | 693 | ``` | |
| 691 | 694 | ||
| 692 | - | That's it. No API key needed for local models. zot defaults to `http://localhost:11434`. | |
| 695 | + | That's it. No API key needed for local models. zut defaults to `http://localhost:11434`. | |
| 693 | 696 | ||
| 694 | 697 | For a remote ollama instance or one behind auth: | |
| 695 | 698 | ||
| 696 | 699 | ```bash | |
| 697 | - | zot --provider ollama --model llama3 --base-url https://my-server.com/v1 --api-key my-token | |
| 700 | + | zut --provider ollama --model llama3 --base-url https://my-server.com/v1 --api-key my-token | |
| 698 | 701 | ``` | |
| 699 | 702 | ||
| 700 | 703 | You can also add models to your `models.json` so you don't need flags every time: | |
| @@ -720,7 +723,7 @@ The `ollama` provider uses the OpenAI chat completions protocol internally, so i | |||
| 720 | 723 | ||
| 721 | 724 | ### Local models with llama.cpp router mode | |
| 722 | 725 | ||
| 723 | - | zot can connect to a recent [llama.cpp](https://github.com/ggml-org/llama.cpp) router, manage its GGUF files, and use loaded models through the router's OpenAI-compatible inference API. This is separate from Ollama. An Ollama server normally listens on port `11434` and should use zot's `ollama` provider instead. | |
| 726 | + | zut can connect to a recent [llama.cpp](https://github.com/ggml-org/llama.cpp) router, manage its GGUF files, and use loaded models through the router's OpenAI-compatible inference API. This is separate from Ollama. An Ollama server normally listens on port `11434` and should use zut's `ollama` provider instead. | |
| 724 | 727 | ||
| 725 | 728 | Install or update llama.cpp. On macOS with Homebrew: | |
| 726 | 729 | ||
| @@ -730,7 +733,7 @@ brew install llama.cpp | |||
| 730 | 733 | brew upgrade llama.cpp | |
| 731 | 734 | ``` | |
| 732 | 735 | ||
| 733 | - | Create a directory for GGUF files and start `llama-server` without `--model`, `-m`, or `-hf`. Supplying one of those options starts a single model rather than the router API zot needs. | |
| 736 | + | Create a directory for GGUF files and start `llama-server` without `--model`, `-m`, or `-hf`. Supplying one of those options starts a single model rather than the router API zut needs. | |
| 734 | 737 | ||
| 735 | 738 | ```bash | |
| 736 | 739 | mkdir -p ~/llama-models | |
| @@ -747,7 +750,7 @@ llama-server \ | |||
| 747 | 750 | ||
| 748 | 751 | `--no-models-autoload` leaves load decisions to `/llama`. `--jinja` enables model chat templates and improves tool-call compatibility. `-ngl 999` requests maximum GPU offload, while `-c 32768` limits each loaded model to a 32K context. Adjust these values for your hardware. | |
| 749 | 752 | ||
| 750 | - | Confirm that router mode is active before configuring zot: | |
| 753 | + | Confirm that router mode is active before configuring zut: | |
| 751 | 754 | ||
| 752 | 755 | ```bash | |
| 753 | 756 | curl http://127.0.0.1:8080/health | |
| @@ -756,7 +759,7 @@ curl http://127.0.0.1:8080/models | |||
| 756 | 759 | ||
| 757 | 760 | The models request must return JSON with a `data` array. A 404 usually means the server is outdated, running on a different port, or was started in single-model mode. | |
| 758 | 761 | ||
| 759 | - | In zot, run `/login`, choose **api key**, and select **llama.cpp**. Enter `http://127.0.0.1:8080` as the router URL and leave the API key empty for a local-only server. Do not enter `/v1`; zot derives the inference URL itself. The saved URL and optional key live in `$ZOT_HOME/auth.json`. | |
| 762 | + | In zut, run `/login`, choose **api key**, and select **llama.cpp**. Enter `http://127.0.0.1:8080` as the router URL and leave the API key empty for a local-only server. Do not enter `/v1`; zut derives the inference URL itself. The saved URL and optional key live in `$ZUT_HOME/auth.json`. | |
| 760 | 763 | ||
| 761 | 764 | You can configure the same connection through environment variables: | |
| 762 | 765 | ||
| @@ -775,7 +778,7 @@ Run `/llama` to: | |||
| 775 | 778 | - explicitly load or unload a model | |
| 776 | 779 | - press `d` to ask the router to remove a downloaded cache model after confirmation | |
| 777 | 780 | ||
| 778 | - | Models discovered through `--models-dir` or a preset cannot be removed by zot. Delete those files from their configured source instead. The router removes the selected GGUF from its Hugging Face cache, but some llama.cpp versions retain shared repository artifacts such as `mmproj` files. Remove the repository's cache directory manually if those artifacts are no longer needed. Hugging Face search uses `HF_TOKEN` when available. Gated repositories require prior access approval, and the `llama-server` process also needs an authorized `HF_TOKEN` because the server performs the download. | |
| 781 | + | Models discovered through `--models-dir` or a preset cannot be removed by zut. Delete those files from their configured source instead. The router removes the selected GGUF from its Hugging Face cache, but some llama.cpp versions retain shared repository artifacts such as `mmproj` files. Remove the repository's cache directory manually if those artifacts are no longer needed. Hugging Face search uses `HF_TOKEN` when available. Gated repositories require prior access approval, and the `llama-server` process also needs an authorized `HF_TOKEN` because the server performs the download. | |
| 779 | 782 | ||
| 780 | 783 | Opening `/model` refreshes the router and lists every loaded model under provider `llama.cpp`. Unloaded models are intentionally omitted because they cannot answer inference requests. Load them through `/llama` first, then select them through `/model`. | |
| 781 | 784 | ||
| @@ -787,7 +790,7 @@ In the main chat, `ctrl+v` checks the system clipboard for an image before falli | |||
| 787 | 790 | ||
| 788 | 791 | ## Inline images | |
| 789 | 792 | ||
| 790 | - | When a tool returns an image (for example `read` on a PNG), zot renders it inline on terminals that support it: **Ghostty**, **Kitty**, **iTerm2**, **WezTerm**. On other terminals you see a text placeholder with MIME type, pixel dimensions, and byte size. Control with the `ZOT_INLINE_IMAGES` env var: | |
| 793 | + | When a tool returns an image (for example `read` on a PNG), zut renders it inline on terminals that support it: **Ghostty**, **Kitty**, **iTerm2**, **WezTerm**. On other terminals you see a text placeholder with MIME type, pixel dimensions, and byte size. Control with the `ZUT_INLINE_IMAGES` env var: | |
| 791 | 794 | ||
| 792 | 795 | | Value | Effect | | |
| 793 | 796 | |---|---| | |
| @@ -796,15 +799,15 @@ When a tool returns an image (for example `read` on a PNG), zot renders it inlin | |||
| 796 | 799 | | `kitty` | Force the Kitty graphics protocol. | | |
| 797 | 800 | | `off`, `none` | Always use the text placeholder. | | |
| 798 | 801 | ||
| 799 | - | Herdr's Kitty graphics support is currently experimental, so zot does not enable inline images there automatically. After enabling `experimental.kitty_graphics` in Herdr, set `ZOT_INLINE_IMAGES=kitty` to opt in. | |
| 802 | + | Herdr's Kitty graphics support is currently experimental, so zut does not enable inline images there automatically. After enabling `experimental.kitty_graphics` in Herdr, set `ZUT_INLINE_IMAGES=kitty` to opt in. | |
| 800 | 803 | ||
| 801 | - | Frames containing images are full-repainted (no differential diff) to prevent stale image pixels from lingering through scroll. That costs one terminal flash per image-containing frame; set `ZOT_INLINE_IMAGES=off` if that bothers you. | |
| 804 | + | Frames containing images are full-repainted (no differential diff) to prevent stale image pixels from lingering through scroll. That costs one terminal flash per image-containing frame; set `ZUT_INLINE_IMAGES=off` if that bothers you. | |
| 802 | 805 | ||
| 803 | 806 | ## Tool rendering | |
| 804 | 807 | ||
| 805 | - | By default each tool call (bash, read, write, edit) renders inside a bordered panel — a `┌─ header ─┐`, `│`-prefixed body rows, and a `└─┘` footer. On a screen with many calls the borders can read as busy, so zot also offers a **flat** mode: a single quiet header line per call (`▌ bash …`) with indented, border-free output. Same information — tool name, arg summary, streamed output, the `... (N more lines, ctrl+o to expand)` truncation — just no frame. | |
| 808 | + | By default each tool call (bash, read, write, edit) renders inside a bordered panel — a `┌─ header ─┐`, `│`-prefixed body rows, and a `└─┘` footer. On a screen with many calls the borders can read as busy, so zut also offers a **flat** mode: a single quiet header line per call (`▌ bash …`) with indented, border-free output. Same information — tool name, arg summary, streamed output, the `... (N more lines, ctrl+o to expand)` truncation — just no frame. | |
| 806 | 809 | ||
| 807 | - | Set the `tool_render` key in `$ZOT_HOME/config.json`: | |
| 810 | + | Set the `tool_render` key in `$ZUT_HOME/config.json`: | |
| 808 | 811 | ||
| 809 | 812 | ```json | |
| 810 | 813 | { | |
| @@ -817,7 +820,7 @@ Set the `tool_render` key in `$ZOT_HOME/config.json`: | |||
| 817 | 820 | | unset / `"box"` (default) | Each tool call is wrapped in a bordered panel. | | |
| 818 | 821 | | `"flat"` | Boxless: a quiet header line plus indented output. | | |
| 819 | 822 | ||
| 820 | - | The `ZOT_FLAT_TOOLS` env var overrides the config for a single run, which is handy for trying it without editing the file: | |
| 823 | + | The `ZUT_FLAT_TOOLS` env var overrides the config for a single run, which is handy for trying it without editing the file: | |
| 821 | 824 | ||
| 822 | 825 | | Value | Effect | | |
| 823 | 826 | |---|---| | |
| @@ -826,18 +829,18 @@ The `ZOT_FLAT_TOOLS` env var overrides the config for a single run, which is han | |||
| 826 | 829 | | unset | Fall back to the `tool_render` config key. | | |
| 827 | 830 | ||
| 828 | 831 | ```sh | |
| 829 | - | ZOT_FLAT_TOOLS=1 zot # flat, just this run | |
| 830 | - | ZOT_FLAT_TOOLS=0 zot # boxes, even if config.json says "flat" | |
| 832 | + | ZUT_FLAT_TOOLS=1 zut # flat, just this run | |
| 833 | + | ZUT_FLAT_TOOLS=0 zut # boxes, even if config.json says "flat" | |
| 831 | 834 | ``` | |
| 832 | 835 | ||
| 833 | 836 | Either way, theme colors still drive the rendering (the header uses your accent/foreground, output uses the tool-output color) and `ctrl+o` still expands a truncated result. | |
| 834 | 837 | ||
| 835 | 838 | ### Tool arg width | |
| 836 | 839 | ||
| 837 | - | The header line for a tool call shows the tool name plus a one-line summary of its primary argument — a `path`, a `command`, or a query. That summary is truncated to 60 cells by default (`web_answer What is the best architecture to implement resilience wit...`). On a wide terminal that can clip long queries more than you'd like, so set the `ZOT_TOOL_ARG_WIDTH` env var to raise (or lower) the limit: | |
| 840 | + | The header line for a tool call shows the tool name plus a one-line summary of its primary argument — a `path`, a `command`, or a query. That summary is truncated to 60 cells by default (`web_answer What is the best architecture to implement resilience wit...`). On a wide terminal that can clip long queries more than you'd like, so set the `ZUT_TOOL_ARG_WIDTH` env var to raise (or lower) the limit: | |
| 838 | 841 | ||
| 839 | 842 | ```sh | |
| 840 | - | ZOT_TOOL_ARG_WIDTH=120 zot # allow up to 120 cells before truncating | |
| 843 | + | ZUT_TOOL_ARG_WIDTH=120 zut # allow up to 120 cells before truncating | |
| 841 | 844 | ``` | |
| 842 | 845 | ||
| 843 | 846 | | Value | Effect | | |
| @@ -861,17 +864,17 @@ By default a message you send renders as a padded, background-tinted bubble: a b | |||
| 861 | 864 | | unset / `false` (default) | Padded, background-tinted user bubble. | | |
| 862 | 865 | | `true` | One quiet gutter line per wrapped row. | | |
| 863 | 866 | ||
| 864 | - | The `ZOT_COMPACT_INPUT` env var overrides the config for a single run (`1`/`true`/`on`/`compact` force compact; `0`/`false`/`off`/`bubble` force the bubble): | |
| 867 | + | The `ZUT_COMPACT_INPUT` env var overrides the config for a single run (`1`/`true`/`on`/`compact` force compact; `0`/`false`/`off`/`bubble` force the bubble): | |
| 865 | 868 | ||
| 866 | 869 | ```sh | |
| 867 | - | ZOT_COMPACT_INPUT=1 zot # compact, just this run | |
| 870 | + | ZUT_COMPACT_INPUT=1 zut # compact, just this run | |
| 868 | 871 | ``` | |
| 869 | 872 | ||
| 870 | 873 | ## Queued messages | |
| 871 | 874 | ||
| 872 | - | You can keep typing while the agent is working. Pressing `enter` during a turn queues the message instead of interrupting: it shows up above the status bar as `sliding in: <text>` and is delivered as the next user turn the moment the current one finishes. Queue as many as you want; they run in order. `esc` cancels the active turn and drops the queue so a runaway turn doesn't flood you with stale follow-ups; `ctrl+c` while busy arms the exit hint instead of interrupting, a second `ctrl+c` within two seconds exits zot. | |
| 875 | + | You can keep typing while the agent is working. Pressing `enter` during a turn queues the message instead of interrupting: it shows up above the status bar as `sliding in: <text>` and is delivered as the next user turn the moment the current one finishes. Queue as many as you want; they run in order. `esc` cancels the active turn and drops the queue so a runaway turn doesn't flood you with stale follow-ups; `ctrl+c` while busy arms the exit hint instead of interrupting, a second `ctrl+c` within two seconds exits zut. | |
| 873 | 876 | ||
| 874 | - | To recover the most recently queued message back into the editor (to tweak it before it runs), press `Option+↑`. In VS Code's integrated terminal that chord doesn't survive xterm.js's macOS key handling — use `Option+Shift+↑` there. zot's hint line under the sliding-in queue adapts automatically based on `$TERM_PROGRAM`. | |
| 877 | + | To recover the most recently queued message back into the editor (to tweak it before it runs), press `Option+↑`. In VS Code's integrated terminal that chord doesn't survive xterm.js's macOS key handling — use `Option+Shift+↑` there. zut's hint line under the sliding-in queue adapts automatically based on `$TERM_PROGRAM`. | |
| 875 | 878 | ||
| 876 | 879 | Slash commands also work while the agent is busy. Non-destructive ones (`/help`, `/jump`, `/btw`, `/sessions`, `/skills`, `/reasoning`, `/settings`, `/jail`, `/unjail`, `/exit`) take effect immediately. Destructive ones (`/clear`, `/compact`, `/login`, `/logout`, `/model`, `/reload-ext`) cancel the active turn first and then run. | |
| 877 | 880 | ||
| @@ -927,49 +930,49 @@ Type `@` followed by a filter string to narrow the list (e.g. `@read` shows only | |||
| 927 | 930 | ||
| 928 | 931 | ## Extensions | |
| 929 | 932 | ||
| 930 | - | zot can be extended in any language via a subprocess + JSON-RPC protocol. Extensions can register slash commands, expose tools to the model, intercept tool calls (block or rewrite args), gate whole turns before the model is called, and rewrite the assistant's visible text before it reaches the user. None are installed by default; opt in explicitly. Hot-reload any time with `/reload-ext`. | |
| 933 | + | zut can be extended in any language via a subprocess + JSON-RPC protocol. Extensions can register slash commands, expose tools to the model, intercept tool calls (block or rewrite args), gate whole turns before the model is called, and rewrite the assistant's visible text before it reaches the user. None are installed by default; opt in explicitly. Hot-reload any time with `/reload-ext`. | |
| 931 | 934 | ||
| 932 | 935 | ### Install and manage | |
| 933 | 936 | ||
| 934 | 937 | ```bash | |
| 935 | - | zot ext install <path|git-url> # copy / clone into $ZOT_HOME/extensions/ | |
| 936 | - | zot ext install --build=go <path> # explicitly build and install local Go source | |
| 937 | - | zot ext list # show installed extensions | |
| 938 | - | zot ext doctor # diagnose load, registration, and conflict issues | |
| 939 | - | zot ext logs <name> [-f] # cat or tail the extension's stderr log | |
| 940 | - | zot ext enable <name> # re-enable a disabled extension | |
| 941 | - | zot ext disable <name> # disable without removing | |
| 942 | - | zot ext remove <name> # delete an extension directory | |
| 938 | + | zut ext install <path|git-url> # copy / clone into $ZUT_HOME/extensions/ | |
| 939 | + | zut ext install --build=go <path> # explicitly build and install local Go source | |
| 940 | + | zut ext list # show installed extensions | |
| 941 | + | zut ext doctor # diagnose load, registration, and conflict issues | |
| 942 | + | zut ext logs <name> [-f] # cat or tail the extension's stderr log | |
| 943 | + | zut ext enable <name> # re-enable a disabled extension | |
| 944 | + | zut ext disable <name> # disable without removing | |
| 945 | + | zut ext remove <name> # delete an extension directory | |
| 943 | 946 | ``` | |
| 944 | 947 | ||
| 945 | - | For local installs, zot validates `extension.json` and any executable path | |
| 948 | + | For local installs, zut validates `extension.json` and any executable path | |
| 946 | 949 | relative to the extension directory before reporting success. It never runs a | |
| 947 | - | build implicitly. Use `zot ext install --build=go <path>` to explicitly build a | |
| 950 | + | build implicitly. Use `zut ext install --build=go <path>` to explicitly build a | |
| 948 | 951 | local Go extension; other source-based extensions must provide their runtime | |
| 949 | 952 | artifact or launcher themselves. A failed validation or build leaves no | |
| 950 | 953 | partial installation behind. | |
| 951 | 954 | ||
| 952 | - | `zot ext doctor` keeps normal extension startup fail-soft, but gives | |
| 955 | + | `zut ext doctor` keeps normal extension startup fail-soft, but gives | |
| 953 | 956 | you an explicit troubleshooting view: manifest errors, disabled or | |
| 954 | 957 | shadowed extensions, subprocess load errors, ready/auto-ready status, | |
| 955 | 958 | registered commands/tools, registration conflicts, warnings, and the | |
| 956 | 959 | stderr log path. | |
| 957 | 960 | ||
| 958 | - | For development, point `zot --ext <path>` at a working directory and skip the install step entirely. Repeatable; takes precedence over installed extensions of the same name. | |
| 961 | + | For development, point `zut --ext <path>` at a working directory and skip the install step entirely. Repeatable; takes precedence over installed extensions of the same name. | |
| 959 | 962 | ||
| 960 | 963 | ### Updating extensions | |
| 961 | 964 | ||
| 962 | - | `zot update` refreshes the zot binary **and** every installed extension that lives in a git checkout. Per-extension behaviour: | |
| 965 | + | `zut update` refreshes the zut binary **and** every installed extension that lives in a git checkout. Per-extension behaviour: | |
| 963 | 966 | ||
| 964 | 967 | - Disabled extensions are skipped. | |
| 965 | - | - Extensions without a `.git/` directory (installed by `zot ext install ./local-path`) are skipped — there is no remote to pull from. | |
| 966 | - | - For the rest, zot stashes any dirty worktree state (including untracked runtime files like `todos.json` or `config.json`), runs `git pull --ff-only`, and pops the stash. If the pop produces conflicts, the conflict markers are left in place and you'll see a warning. | |
| 967 | - | - Diverged branches, offline pulls, or any other git failure are reported as `failed` and the next extension is processed. `zot update` itself never aborts because of an extension. | |
| 968 | - | - zot does **not** run any build step (`go build`, `npm install`, `make`) after the pull. Extension authors are expected to commit the runnable artifact (binary, transpiled JS, etc.). If you need a build, run it explicitly, reinstall the extension, or use `/reload-ext` for a working-tree copy. | |
| 968 | + | - Extensions without a `.git/` directory (installed by `zut ext install ./local-path`) are skipped — there is no remote to pull from. | |
| 969 | + | - For the rest, zut stashes any dirty worktree state (including untracked runtime files like `todos.json` or `config.json`), runs `git pull --ff-only`, and pops the stash. If the pop produces conflicts, the conflict markers are left in place and you'll see a warning. | |
| 970 | + | - Diverged branches, offline pulls, or any other git failure are reported as `failed` and the next extension is processed. `zut update` itself never aborts because of an extension. | |
| 971 | + | - zut does **not** run any build step (`go build`, `npm install`, `make`) after the pull. Extension authors are expected to commit the runnable artifact (binary, transpiled JS, etc.). If you need a build, run it explicitly, reinstall the extension, or use `/reload-ext` for a working-tree copy. | |
| 969 | 972 | ||
| 970 | 973 | ### Theme-only extensions | |
| 971 | 974 | ||
| 972 | - | An extension may ship only a theme: `extension.json` plus `theme.json` (or `themes/theme.json`) and no executable. zot loads it without spawning a subprocess and shows it in `/settings` with source information. See [docs/themes.md](docs/themes.md). | |
| 975 | + | An extension may ship only a theme: `extension.json` plus `theme.json` (or `themes/theme.json`) and no executable. zut loads it without spawning a subprocess and shows it in `/settings` with source information. See [docs/themes.md](docs/themes.md). | |
| 973 | 976 | ||
| 974 | 977 | ### Reference | |
| 975 | 978 | ||
| @@ -977,12 +980,12 @@ An extension may ship only a theme: `extension.json` plus `theme.json` (or `them | |||
| 977 | 980 | ||
| 978 | 981 | ## Skills | |
| 979 | 982 | ||
| 980 | - | A skill is a per-folder `SKILL.md` file with a YAML frontmatter header. zot discovers skills at startup, surfaces their names in the system prompt, and exposes a built-in `skill` tool the model uses to load the body on demand. | |
| 983 | + | A skill is a per-folder `SKILL.md` file with a YAML frontmatter header. zut discovers skills at startup, surfaces their names in the system prompt, and exposes a built-in `skill` tool the model uses to load the body on demand. | |
| 981 | 984 | ||
| 982 | - | By default zot loads built-in skills plus user-installed skills from: | |
| 985 | + | By default zut loads built-in skills plus user-installed skills from: | |
| 983 | 986 | ||
| 984 | - | - `./.zot/skills/<name>/SKILL.md` (project) | |
| 985 | - | - `$ZOT_HOME/skills/<name>/SKILL.md` (global) | |
| 987 | + | - `./.zut/skills/<name>/SKILL.md` (project) | |
| 988 | + | - `$ZUT_HOME/skills/<name>/SKILL.md` (global) | |
| 986 | 989 | - `./.claude/skills/<name>/SKILL.md`, `~/.claude/skills/<name>/SKILL.md` (Claude-compatible layout) | |
| 987 | 990 | - `./.agents/skills/<name>/SKILL.md`, `~/.agents/skills/<name>/SKILL.md` (agent-compatible layout) | |
| 988 | 991 | ||
| @@ -992,14 +995,14 @@ See [docs/skills.md](docs/skills.md) for the frontmatter fields, authoring tips, | |||
| 992 | 995 | ||
| 993 | 996 | ## Telegram bot (bridge) | |
| 994 | 997 | ||
| 995 | - | zot can run as a telegram bot so you can DM it from your phone. Two ways to run it: **from inside the TUI** (the running session mirrors into Telegram) or **as a standalone background daemon** (a headless bot with its own independent agent). | |
| 998 | + | zut can run as a telegram bot so you can DM it from your phone. Two ways to run it: **from inside the TUI** (the running session mirrors into Telegram) or **as a standalone background daemon** (a headless bot with its own independent agent). | |
| 996 | 999 | ||
| 997 | 1000 | ### From inside the TUI | |
| 998 | 1001 | ||
| 999 | 1002 | Type `/telegram` in the running TUI to open a picker with **connect**, **disconnect**, and **status**. When connected: | |
| 1000 | 1003 | ||
| 1001 | 1004 | - DMs from the paired user become prompts in the **same** session you're typing in, so you can continue a conversation from the terminal on your phone and back again. | |
| 1002 | - | - Messages you type in the TUI are mirrored into the Telegram thread prefixed `you: ...` and the assistant's replies come back prefixed `zot: ...`, so the Telegram chat stays a complete record of both sides of the conversation. | |
| 1005 | + | - Messages you type in the TUI are mirrored into the Telegram thread prefixed `you: ...` and the assistant's replies come back prefixed `zut: ...`, so the Telegram chat stays a complete record of both sides of the conversation. | |
| 1003 | 1006 | - Messages sent from Telegram show up as your own bubble in Telegram (no mirror) and the assistant's reply to them comes back bare (no prefix). | |
| 1004 | 1007 | - The status bar shows a `- tg -` tag while the bridge is active. | |
| 1005 | 1008 | - `/telegram connect` / `/telegram disconnect` / `/telegram status` (or `/tg`) also work as direct commands without the picker. | |
| @@ -1011,32 +1014,32 @@ The in-TUI bridge refuses to start while the standalone daemon (below) is runnin | |||
| 1011 | 1014 | For headless servers or long-running bots unattached to a TUI: | |
| 1012 | 1015 | ||
| 1013 | 1016 | ```bash | |
| 1014 | - | zot telegram-bot setup # paste a BotFather token, verify, save | |
| 1015 | - | zot telegram-bot run # foreground: long-poll in this terminal (ctrl+c to stop) | |
| 1016 | - | zot telegram-bot start # background: detach and return immediately | |
| 1017 | - | zot telegram-bot stop # SIGTERM the background bot (SIGKILL after 5s) | |
| 1018 | - | zot telegram-bot logs -f # tail $ZOT_HOME/logs/bot.log (omit -f to just cat) | |
| 1019 | - | zot telegram-bot status # config (token masked) + running/stopped | |
| 1020 | - | zot telegram-bot reset # forget the token and paired user | |
| 1021 | - | # short alias: `zot tg ...` is accepted for every subcommand | |
| 1017 | + | zut telegram-bot setup # paste a BotFather token, verify, save | |
| 1018 | + | zut telegram-bot run # foreground: long-poll in this terminal (ctrl+c to stop) | |
| 1019 | + | zut telegram-bot start # background: detach and return immediately | |
| 1020 | + | zut telegram-bot stop # SIGTERM the background bot (SIGKILL after 5s) | |
| 1021 | + | zut telegram-bot logs -f # tail $ZUT_HOME/logs/bot.log (omit -f to just cat) | |
| 1022 | + | zut telegram-bot status # config (token masked) + running/stopped | |
| 1023 | + | zut telegram-bot reset # forget the token and paired user | |
| 1024 | + | # short alias: `zut tg ...` is accepted for every subcommand | |
| 1022 | 1025 | ``` | |
| 1023 | 1026 | ||
| 1024 | - | The background flavor writes the child's PID to `$ZOT_HOME/bot.pid` and redirects stdout and stderr to `$ZOT_HOME/logs/bot.log`. `zot telegram-bot stop` reads that PID, sends SIGTERM, waits up to five seconds, then escalates to SIGKILL if the child is still alive. Running two instances at once is refused at startup. | |
| 1027 | + | The background flavor writes the child's PID to `$ZUT_HOME/bot.pid` and redirects stdout and stderr to `$ZUT_HOME/logs/bot.log`. `zut telegram-bot stop` reads that PID, sends SIGTERM, waits up to five seconds, then escalates to SIGKILL if the child is still alive. Running two instances at once is refused at startup. | |
| 1025 | 1028 | ||
| 1026 | - | > **Use the installed binary for `start`.** `go run ./cmd/zot telegram-bot start` won't work. `go run` builds a binary in a temp directory and deletes it when it exits, which kills the detached child. Run `make install` (or `go build`) first and invoke the installed binary. | |
| 1029 | + | > **Use the installed binary for `start`.** `go run ./cmd/zut telegram-bot start` won't work. `go run` builds a binary in a temp directory and deletes it when it exits, which kills the detached child. Run `make install` (or `go build`) first and invoke the installed binary. | |
| 1027 | 1030 | ||
| 1028 | 1031 | Setup flow: | |
| 1029 | 1032 | ||
| 1030 | 1033 | 1. Talk to [@BotFather](https://t.me/BotFather) on telegram, run `/newbot`, copy the token it gives you. | |
| 1031 | - | 2. Run `zot telegram-bot setup` and paste the token when prompted. | |
| 1032 | - | 3. Run `zot telegram-bot run` in the directory you want the agent to operate in. | |
| 1034 | + | 2. Run `zut telegram-bot setup` and paste the token when prompted. | |
| 1035 | + | 3. Run `zut telegram-bot run` in the directory you want the agent to operate in. | |
| 1033 | 1036 | 4. Open your bot on telegram, send `/start`. The first user to do this claims the bridge (stored as `allowed_user_id`); every other user is rejected. | |
| 1034 | 1037 | ||
| 1035 | - | From then on, any DM you send is forwarded to the agent as a user prompt. Attached photos or `image/*` documents are downloaded and passed to vision-capable models. In-bot telegram commands are case-insensitive: `/help`, `/status`, `/stop` (cancel the current turn). Config lives in `$ZOT_HOME/bot.json` (mode 0600). | |
| 1038 | + | From then on, any DM you send is forwarded to the agent as a user prompt. Attached photos or `image/*` documents are downloaded and passed to vision-capable models. In-bot telegram commands are case-insensitive: `/help`, `/status`, `/stop` (cancel the current turn). Config lives in `$ZUT_HOME/bot.json` (mode 0600). | |
| 1036 | 1039 | ||
| 1037 | 1040 | Starting either Telegram bridge removes any webhook configured for that bot before long polling begins, while preserving pending updates. Telegram does not allow webhooks and `getUpdates` polling at the same time, so do not share the bot token with another service that expects to keep a webhook active. | |
| 1038 | 1041 | ||
| 1039 | - | Bot mode respects the usual zot flags: `--provider`, `--model`, `--cwd`, `--reasoning`, `--continue`, `--no-session`, `--no-tools`, and so on. Run `zot tg run -c --model claude-opus-4-1` to resume the latest session on Opus, for example. | |
| 1042 | + | Bot mode respects the usual zut flags: `--provider`, `--model`, `--cwd`, `--reasoning`, `--continue`, `--no-session`, `--no-tools`, and so on. Run `zut tg run -c --model claude-opus-4-1` to resume the latest session on Opus, for example. | |
| 1040 | 1043 | ||
| 1041 | 1044 | ### Architecture: protocol-agnostic bot core | |
| 1042 | 1045 | ||
| @@ -1048,7 +1051,7 @@ This means additional messaging backends (Discord, Slack, Signal, and similar) c | |||
| 1048 | 1051 | ||
| 1049 | 1052 | ```bash | |
| 1050 | 1053 | make help # list the important developer commands | |
| 1051 | - | make build # build ./bin/zot as 0.0.0-dev | |
| 1054 | + | make build # build ./bin/zut as 0.0.0-dev | |
| 1052 | 1055 | make test # go test -race ./... | |
| 1053 | 1056 | make test-fast # go test ./... | |
| 1054 | 1057 | make lint # golangci-lint + go vet + gofmt check | |
| @@ -1062,7 +1065,7 @@ make release VERSION=0.1.0 # cross-compile release binaries | |||
| 1062 | 1065 | Source layout (single Go module, four packages under `packages/`): | |
| 1063 | 1066 | ||
| 1064 | 1067 | ``` | |
| 1065 | - | cmd/zot/ main() | |
| 1068 | + | cmd/zut/ main() | |
| 1066 | 1069 | packages/provider/ LLM client surface, model catalog, streaming clients | |
| 1067 | 1070 | packages/provider/auth/ credential store, api-key probe, oauth, login server | |
| 1068 | 1071 | packages/core/ agent loop, sessions, cost tracking, compaction | |
| @@ -1077,12 +1080,12 @@ packages/agent/modes/telegram/ telegram adapter, api client, daemon | |||
| 1077 | 1080 | packages/agent/tools/ read, write, edit, bash, lsp, sandbox | |
| 1078 | 1081 | packages/agent/skills/ skill discovery, frontmatter parser, skill tool | |
| 1079 | 1082 | packages/agent/subagents/ named profiles, supervisor, and background runtime | |
| 1080 | - | packages/agent/sdk/ public Go SDK for embedding zot in-process (package sdk) | |
| 1083 | + | packages/agent/sdk/ public Go SDK for embedding zut in-process (package sdk) | |
| 1081 | 1084 | packages/agent/ext/ public Go SDK for writing extensions (package ext) | |
| 1082 | 1085 | ``` | |
| 1083 | 1086 | ||
| 1084 | 1087 | Downstream consumers can depend on individual packages: | |
| 1085 | - | `go get github.com/patriceckhart/zot/packages/core` pulls only `core` and its transitive deps (today: `provider`), no agent or TUI code. | |
| 1088 | + | `go get github.com/bnema/zut/packages/core` pulls only `core` and its transitive deps (today: `provider`), no agent or TUI code. | |
| 1086 | 1089 | ||
| 1087 | 1090 | ## License | |
| 1088 | 1091 | ||
R cmd/zot/main.go → cmd/zut/main.go +5 -5 renamed, 88% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // Command zot is a lightweight terminal coding agent. | |
| 1 | + | // Command zut is a lightweight terminal coding agent. | |
| 2 | 2 | package main | |
| 3 | 3 | ||
| 4 | 4 | import ( | |
| @@ -8,12 +8,12 @@ import ( | |||
| 8 | 8 | "runtime/debug" | |
| 9 | 9 | "strings" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent" | |
| 11 | + | "github.com/bnema/zut/packages/agent" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // Injected at build time via -ldflags "-X main.version=... -X main.commit=... -X main.date=...". | |
| 15 | 15 | // See .goreleaser.yaml for the release build and the Makefile for | |
| 16 | - | // local builds. Defaults make `zot --version` print something sensible | |
| 16 | + | // local builds. Defaults make `zut --version` print something sensible | |
| 17 | 17 | // when built without ldflags. | |
| 18 | 18 | var ( | |
| 19 | 19 | // 0.0.0-dev is the placeholder for local / untagged builds. | |
| @@ -37,12 +37,12 @@ func main() { | |||
| 37 | 37 | v = v + ")" | |
| 38 | 38 | } | |
| 39 | 39 | if err := agent.Run(os.Args[1:], v); err != nil { | |
| 40 | - | fmt.Fprintln(os.Stderr, "zot:", err) | |
| 40 | + | fmt.Fprintln(os.Stderr, "zut:", err) | |
| 41 | 41 | os.Exit(1) | |
| 42 | 42 | } | |
| 43 | 43 | } | |
| 44 | 44 | ||
| 45 | - | // resolvedVersion falls back to the module version embedded by Go when zot is | |
| 45 | + | // resolvedVersion falls back to the module version embedded by Go when zut is | |
| 46 | 46 | // installed with "go install ...@version". Release archives still use the | |
| 47 | 47 | // version injected by GoReleaser, and local source builds remain 0.0.0-dev. | |
| 48 | 48 | func resolvedVersion(linkedVersion string) string { | |
R cmd/zot/main_test.go → cmd/zut/main_test.go +0 -0 renamed, 100% similar
renamed, 100% similar
M docs.go +4 -4
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | 9 | "sort" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | - | // embeddedDocs holds zot's user-facing documentation so installed | |
| 12 | + | // embeddedDocs holds zut's user-facing documentation so installed | |
| 13 | 13 | // binaries do not depend on a source checkout existing on disk. | |
| 14 | 14 | // | |
| 15 | 15 | //go:embed README.md docs/*.md | |
| @@ -23,11 +23,11 @@ var docFiles = map[string]string{ | |||
| 23 | 23 | "themes.md": "docs/themes.md", | |
| 24 | 24 | } | |
| 25 | 25 | ||
| 26 | - | // EnsureInstalled writes the embedded docs into $ZOT_HOME/docs and | |
| 26 | + | // EnsureInstalled writes the embedded docs into $ZUT_HOME/docs and | |
| 27 | 27 | // returns that directory. Existing files are left untouched when their | |
| 28 | 28 | // content already matches the embedded copy. | |
| 29 | - | func EnsureInstalled(zotHome string) (string, error) { | |
| 30 | - | dir := filepath.Join(zotHome, "docs") | |
| 29 | + | func EnsureInstalled(zutHome string) (string, error) { | |
| 30 | + | dir := filepath.Join(zutHome, "docs") | |
| 31 | 31 | if err := os.MkdirAll(dir, 0o755); err != nil { | |
| 32 | 32 | return dir, err | |
| 33 | 33 | } | |
M docs/extensions.md +84 -80
| @@ -1,6 +1,6 @@ | |||
| 1 | - | # zot extensions | |
| 1 | + | # zut extensions | |
| 2 | 2 | ||
| 3 | - | zot can be extended with custom slash commands by running an external | |
| 3 | + | zut can be extended with custom slash commands by running an external | |
| 4 | 4 | program as a subprocess and exchanging newline-delimited JSON over | |
| 5 | 5 | its stdin/stdout. Extensions can be written in **any language** that | |
| 6 | 6 | can read and write JSON lines from stdio — Go, TypeScript, Python, | |
| @@ -12,7 +12,7 @@ Six phases shipped so far: | |||
| 12 | 12 | - **Phase 2**: tools the LLM can call. | |
| 13 | 13 | - **Phase 3**: lifecycle event subscriptions + tool-call interception | |
| 14 | 14 | for guardrail extensions. | |
| 15 | - | - **Phase 4**: interactive extension-owned panels rendered inside zot. | |
| 15 | + | - **Phase 4**: interactive extension-owned panels rendered inside zut. | |
| 16 | 16 | - **Phase 5**: branch-aware session lifecycle events and opaque extension | |
| 17 | 17 | state persisted with session files. | |
| 18 | 18 | - **Phase 6**: hidden per-turn context, persistent status/widgets, and | |
| @@ -28,18 +28,20 @@ no SDK required: | |||
| 28 | 28 | ||
| 29 | 29 | ```python | |
| 30 | 30 | #!/usr/bin/env python3 | |
| 31 | - | # $ZOT_HOME/extensions/hello-py/hello.py | |
| 31 | + | # $ZUT_HOME/extensions/hello-py/hello.py | |
| 32 | 32 | import json, sys | |
| 33 | 33 | ||
| 34 | 34 | def emit(obj): | |
| 35 | 35 | sys.stdout.write(json.dumps(obj) + "\n") | |
| 36 | 36 | sys.stdout.flush() | |
| 37 | 37 | ||
| 38 | - | emit({"type":"hello","name":"hello-py","version":"1.0.0","capabilities":["commands"]}) | |
| 38 | + | emit({"type":"hello","protocol_version":2,"name":"hello-py","version":"1.0.0","capabilities":["commands"]}) | |
| 39 | 39 | ||
| 40 | 40 | for line in sys.stdin: | |
| 41 | 41 | msg = json.loads(line) | |
| 42 | 42 | if msg["type"] == "hello_ack": | |
| 43 | + | if msg.get("protocol_version") != 2: | |
| 44 | + | raise SystemExit("unsupported extension protocol version") | |
| 43 | 45 | emit({"type":"register_command","name":"hellopy","description":"say hi (python)"}) | |
| 44 | 46 | emit({"type":"ready"}) | |
| 45 | 47 | elif msg["type"] == "command_invoked": | |
| @@ -63,58 +65,58 @@ Drop it in a directory with this `extension.json`: | |||
| 63 | 65 | ``` | |
| 64 | 66 | ||
| 65 | 67 | `exec` is required for protocol extensions. If an extension only ships | |
| 66 | - | `theme.json` or `themes/theme.json`, no `exec` is required and zot does | |
| 68 | + | `theme.json` or `themes/theme.json`, no `exec` is required and zut does | |
| 67 | 69 | not spawn a subprocess. | |
| 68 | 70 | ||
| 69 | 71 | `chmod +x hello.py`, install: | |
| 70 | 72 | ||
| 71 | 73 | ```bash | |
| 72 | - | zot ext install ./hello-py | |
| 74 | + | zut ext install ./hello-py | |
| 73 | 75 | ``` | |
| 74 | 76 | ||
| 75 | - | Restart `zot`, type `/hellopy`, the agent greets you. Done. | |
| 77 | + | Restart `zut`, type `/hellopy`, the agent greets you. Done. | |
| 76 | 78 | ||
| 77 | 79 | ## Built-in extensions | |
| 78 | 80 | ||
| 79 | - | **zot ships with no extensions installed by default.** A fresh `zot install` (or `go install`) gives you a clean agent. Extensions are entirely opt-in: you install (or `--ext` for one run) only the ones you want. | |
| 81 | + | **zut ships with no extensions installed by default.** A fresh `zut install` (or `go install`) gives you a clean agent. Extensions are entirely opt-in: you install (or `--ext` for one run) only the ones you want. | |
| 80 | 82 | ||
| 81 | 83 | The `examples/extensions/` directory in the repo is reference code, not a default install set. To use any of those: | |
| 82 | 84 | ||
| 83 | 85 | ```bash | |
| 84 | 86 | # install a Go example and explicitly build it in the staged install | |
| 85 | - | zot ext install --build=go path/to/zot/examples/extensions/hello | |
| 87 | + | zut ext install --build=go path/to/zut/examples/extensions/hello | |
| 86 | 88 | ||
| 87 | 89 | # or build it in the source tree for a one-run development load | |
| 88 | - | cd path/to/zot/examples/extensions/hello | |
| 90 | + | cd path/to/zut/examples/extensions/hello | |
| 89 | 91 | go build -o hello . | |
| 90 | - | zot --ext . | |
| 92 | + | zut --ext . | |
| 91 | 93 | ``` | |
| 92 | 94 | ||
| 93 | - | `zot ext install` never builds source code implicitly. For local installs it | |
| 95 | + | `zut ext install` never builds source code implicitly. For local installs it | |
| 94 | 96 | validates `extension.json` and any executable path relative to the extension | |
| 95 | 97 | directory, then stages the copy so a missing or ignored runtime artifact fails | |
| 96 | 98 | with an explicit error instead of becoming a broken installed extension. For a | |
| 97 | 99 | local Go extension, opt into the fixed builder explicitly with | |
| 98 | - | `zot ext install --build=go <path>`. The builder runs `go build -trimpath` from | |
| 100 | + | `zut ext install --build=go <path>`. The builder runs `go build -trimpath` from | |
| 99 | 101 | the source directory, writes the manifest-declared relative executable into the | |
| 100 | 102 | staging directory, and validates it before installation. It may resolve Go | |
| 101 | 103 | modules and access the network according to the user's Go environment. Bare | |
| 102 | - | launchers such as `go`, `node`, and `npx` remain resolved from `PATH` when zot | |
| 104 | + | launchers such as `go`, `node`, and `npx` remain resolved from `PATH` when zut | |
| 103 | 105 | starts; they cannot be used as the output of `--build=go`. | |
| 104 | 106 | ||
| 105 | 107 | Nothing is auto-installed and nothing reaches out to the network without your explicit action. | |
| 106 | 108 | ||
| 107 | 109 | ## Layout & discovery | |
| 108 | 110 | ||
| 109 | - | zot scans two directories on startup, in this order: | |
| 111 | + | zut scans two directories on startup, in this order: | |
| 110 | 112 | ||
| 111 | - | 1. **Project-local**: `./.zot/extensions/<name>/extension.json` | |
| 112 | - | 2. **Global**: `$ZOT_HOME/extensions/<name>/extension.json` | |
| 113 | + | 1. **Project-local**: `./.zut/extensions/<name>/extension.json` | |
| 114 | + | 2. **Global**: `$ZUT_HOME/extensions/<name>/extension.json` | |
| 113 | 115 | ||
| 114 | 116 | A project-local extension with the same name wins over a global one. | |
| 115 | - | When `XDG_STATE_HOME` is set on any platform, `$ZOT_HOME` defaults to | |
| 116 | - | `$XDG_STATE_HOME/zot`. Otherwise it defaults to `~/Library/Application Support/zot/` | |
| 117 | - | on macOS, `~/.local/state/zot` on Linux, or `%LOCALAPPDATA%\zot` on Windows. | |
| 117 | + | When `XDG_STATE_HOME` is set on any platform, `$ZUT_HOME` defaults to | |
| 118 | + | `$XDG_STATE_HOME/zut`. Otherwise it defaults to `~/Library/Application Support/zut/` | |
| 119 | + | on macOS, `~/.local/state/zut` on Linux, or `%LOCALAPPDATA%\zut` on Windows. | |
| 118 | 120 | ||
| 119 | 121 | Because each extension owns its own directory, the recommended place | |
| 120 | 122 | for extension state is inside that directory itself (for example | |
| @@ -123,7 +125,7 @@ extension). The host also passes this path back in `hello_ack` as | |||
| 123 | 125 | `extension_dir` / `data_dir` so runtime code does not need to guess it. | |
| 124 | 126 | ||
| 125 | 127 | Each extension owns its own subdirectory. The `extension.json` | |
| 126 | - | manifest tells zot how to launch it: | |
| 128 | + | manifest tells zut how to launch it: | |
| 127 | 129 | ||
| 128 | 130 | ```json | |
| 129 | 131 | { | |
| @@ -140,31 +142,32 @@ manifest tells zot how to launch it: | |||
| 140 | 142 | ||
| 141 | 143 | | field | meaning | | |
| 142 | 144 | |---|---| | |
| 143 | - | | `name` | required. how zot identifies the extension; must match what's sent in the `hello` frame. | | |
| 144 | - | | `version` | optional. shown in `zot ext list`. | | |
| 145 | + | | `name` | required. how zut identifies the extension; must match what's sent in the `hello` frame. | | |
| 146 | + | | `version` | optional. shown in `zut ext list`. | | |
| 145 | 147 | | `exec` | required. path to the executable (relative to the manifest). | | |
| 146 | 148 | | `args` | optional. extra argv passed to `exec`. | | |
| 147 | 149 | | `language` | optional. informational only (`go`, `python`, `typescript`, ...). | | |
| 148 | - | | `description` | optional. shown in `zot ext list`. | | |
| 150 | + | | `description` | optional. shown in `zut ext list`. | | |
| 149 | 151 | | `skills` | optional. relative directories containing bundled `<name>/SKILL.md` files or a direct `SKILL.md`. Paths must remain inside the extension directory. | | |
| 150 | 152 | | `enabled` | optional, defaults to `true`. set to `false` to disable without removing. | | |
| 151 | 153 | ||
| 152 | 154 | ## Lifecycle | |
| 153 | 155 | ||
| 154 | - | 1. **Discovery**: zot reads every `extension.json` in the search dirs. | |
| 156 | + | 1. **Discovery**: zut reads every `extension.json` in the search dirs. | |
| 155 | 157 | 2. **Spawn**: enabled extensions are launched as subprocesses. stderr | |
| 156 | - | redirects to `$ZOT_HOME/logs/ext-<name>.log` (one file per | |
| 158 | + | redirects to `$ZUT_HOME/logs/ext-<name>.log` (one file per | |
| 157 | 159 | extension, append-mode). | |
| 158 | 160 | 3. **Hello handshake**: the extension's first stdout frame must be | |
| 159 | - | `hello`; zot replies with `hello_ack` containing the protocol | |
| 160 | - | version, the active provider/model/cwd, and the extension's own | |
| 161 | - | data directory so it can persist files beside its manifest. | |
| 161 | + | `hello` with `protocol_version: 2`; zut rejects a mismatched major | |
| 162 | + | and replies with `hello_ack` containing the same protocol version, | |
| 163 | + | the active provider/model/cwd, and the extension's own data directory | |
| 164 | + | so it can persist files beside its manifest. | |
| 162 | 165 | 4. **Registration**: after receiving `hello_ack`, the extension sends | |
| 163 | 166 | `register_command`, `register_tool`, and subscription frames, then | |
| 164 | 167 | sends `ready`. First-come-first-served: a name already taken by a | |
| 165 | 168 | built-in or by a previously-loaded extension is silently shadowed | |
| 166 | 169 | (logged in the extension's own log file). | |
| 167 | - | 5. **Runtime**: after `ready`, zot dispatches `command_invoked` frames | |
| 170 | + | 5. **Runtime**: after `ready`, zut dispatches `command_invoked` frames | |
| 168 | 171 | when the user runs a registered command; the extension responds | |
| 169 | 172 | with `command_response`. Extensions can also push `notify`, persistent | |
| 170 | 173 | status/widget, and structured `alert` frames during runtime. Extensions | |
| @@ -172,12 +175,12 @@ manifest tells zot how to launch it: | |||
| 172 | 175 | and their own restored opaque state. Panel-capable extensions may open an | |
| 173 | 176 | interactive panel, receive key events, and push redraws while the panel is | |
| 174 | 177 | focused. | |
| 175 | - | 6. **Shutdown**: when zot exits, it sends `shutdown` and waits up to | |
| 178 | + | 6. **Shutdown**: when zut exits, it sends `shutdown` and waits up to | |
| 176 | 179 | 2s for the extension to send `shutdown_ack`. Holdouts are | |
| 177 | 180 | SIGTERM'd, then SIGKILL'd. | |
| 178 | 181 | ||
| 179 | - | A crashing extension does not bring down zot. The slash command it | |
| 180 | - | owned simply stops working until the extension is fixed and zot is | |
| 182 | + | A crashing extension does not bring down zut. The slash command it | |
| 183 | + | owned simply stops working until the extension is fixed and zut is | |
| 181 | 184 | restarted. | |
| 182 | 185 | ||
| 183 | 186 | ## Wire format | |
| @@ -193,7 +196,7 @@ or any other stdout frame before `hello`. | |||
| 193 | 196 | #### `hello` (required, first frame) | |
| 194 | 197 | ||
| 195 | 198 | ```json | |
| 196 | - | {"type":"hello","name":"weather","version":"1.0.0", | |
| 199 | + | {"type":"hello","protocol_version":2,"name":"weather","version":"1.0.0", | |
| 197 | 200 | "capabilities":["commands","tools","alerts","panels"]} | |
| 198 | 201 | ``` | |
| 199 | 202 | ||
| @@ -204,7 +207,7 @@ or any other stdout frame before `hello`. | |||
| 204 | 207 | "description":"current weather for a city"} | |
| 205 | 208 | ``` | |
| 206 | 209 | ||
| 207 | - | Command names are matched case-insensitively. zot sends `command_invoked.name` using the canonical spelling registered here. Registrations that differ only by case conflict, and the first registration remains active. | |
| 210 | + | Command names are matched case-insensitively. zut sends `command_invoked.name` using the canonical spelling registered here. Registrations that differ only by case conflict, and the first registration remains active. | |
| 208 | 211 | ||
| 209 | 212 | #### `register_tool` | |
| 210 | 213 | ||
| @@ -233,11 +236,11 @@ Set `"deferred": true` to register a tool without advertising its definition ini | |||
| 233 | 236 | "activate_tools":["weather"]} | |
| 234 | 237 | ``` | |
| 235 | 238 | ||
| 236 | - | On Kimi K3's OpenAI-compatible routes, zot places newly activated schemas at the tool-result position using Kimi's native deferred-tool format. Other models receive the complete active tool list on the next request. Unknown names are ignored. The Go extension SDK exposes `DeferredTool` and `ToolResult.ActivateTools` for the same protocol. | |
| 239 | + | On Kimi K3's OpenAI-compatible routes, zut places newly activated schemas at the tool-result position using Kimi's native deferred-tool format. Other models receive the complete active tool list on the next request. Unknown names are ignored. The Go extension SDK exposes `DeferredTool` and `ToolResult.ActivateTools` for the same protocol. | |
| 237 | 240 | ||
| 238 | 241 | #### `ready` | |
| 239 | 242 | ||
| 240 | - | Sentinel telling zot "all initial registrations are flushed". Send it | |
| 243 | + | Sentinel telling zut "all initial registrations are flushed". Send it | |
| 241 | 244 | right after your last `register_*` frame so the host can build the | |
| 242 | 245 | agent's tool registry without racing the registration window. | |
| 243 | 246 | ||
| @@ -259,7 +262,7 @@ registered deferred tools that become available after this result. | |||
| 259 | 262 | "details":{"state":{"version":1}}} | |
| 260 | 263 | ``` | |
| 261 | 264 | ||
| 262 | - | `details` is opaque extension metadata. Zot excludes it from provider | |
| 265 | + | `details` is opaque extension metadata. Zut excludes it from provider | |
| 263 | 266 | requests and persists it as the latest extension state for the active | |
| 264 | 267 | session branch. Keep it valid JSON and reasonably small; the host caps | |
| 265 | 268 | persisted snapshots at 256 KiB. The next `session_opened`, | |
| @@ -284,9 +287,9 @@ Recognised event names include `session_start`, `session_opened`, | |||
| 284 | 287 | ||
| 285 | 288 | Session events contain `session` with the current branch ID, parent ID, | |
| 286 | 289 | path, cwd, and fork point. They also contain `state`, but only for the | |
| 287 | - | receiving extension. Zot never exposes another extension's state. | |
| 290 | + | receiving extension. Zut never exposes another extension's state. | |
| 288 | 291 | ||
| 289 | - | `tool_confirmation_requested` fires only when zot is about to wait for | |
| 292 | + | `tool_confirmation_requested` fires only when zut is about to wait for | |
| 290 | 293 | interactive approval. Calls running in yolo mode, calls covered by a | |
| 291 | 294 | remembered approval, and calls blocked before confirmation do not emit it. | |
| 292 | 295 | The event includes `tool_id`, `tool_name`, and the short `tool_preview` | |
| @@ -353,9 +356,9 @@ subsequent interceptor sees the previous one's output. | |||
| 353 | 356 | - `"display"` — appends `display` to the chat as a one-shot styled | |
| 354 | 357 | note. No model call, nothing written to the transcript. | |
| 355 | 358 | - `"open_panel"` — opens an extension-owned interactive panel inside | |
| 356 | - | zot. The panel content lives in `open_panel`. | |
| 359 | + | zut. The panel content lives in `open_panel`. | |
| 357 | 360 | - `"noop"` — the extension handled it itself (e.g. it pushed | |
| 358 | - | `notify` frames or kicked off background work). zot doesn't change | |
| 361 | + | `notify` frames or kicked off background work). zut doesn't change | |
| 359 | 362 | the UI in response. | |
| 360 | 363 | ||
| 361 | 364 | Example: | |
| @@ -370,13 +373,13 @@ Example: | |||
| 370 | 373 | }} | |
| 371 | 374 | ``` | |
| 372 | 375 | ||
| 373 | - | If `error` is non-empty, zot renders it as a red status line | |
| 376 | + | If `error` is non-empty, zut renders it as a red status line | |
| 374 | 377 | regardless of `action`. | |
| 375 | 378 | ||
| 376 | 379 | #### `submit` (one-way, any time) | |
| 377 | 380 | ||
| 378 | 381 | Submits text as a user prompt in the interactive host. If the agent is | |
| 379 | - | idle, zot starts a turn immediately. If a turn is already running, zot | |
| 382 | + | idle, zut starts a turn immediately. If a turn is already running, zut | |
| 380 | 383 | queues the prompt behind it using the same queue path as typed input. | |
| 381 | 384 | Empty or whitespace-only text is ignored. | |
| 382 | 385 | ||
| @@ -419,7 +422,7 @@ Sets or replaces one status item owned by the extension. Sending an empty | |||
| 419 | 422 | #### `widget` (one-way, persistent) | |
| 420 | 423 | ||
| 421 | 424 | Sets or replaces a compact widget. `position` is a host-defined placement | |
| 422 | - | hint with these interactive-zot values: | |
| 425 | + | hint with these interactive-zut values: | |
| 423 | 426 | ||
| 424 | 427 | - `"above_input"` keeps the widget in the existing persistent chrome above | |
| 425 | 428 | the editor. | |
| @@ -498,18 +501,19 @@ Sent in response to `shutdown`. Extension should exit promptly after. | |||
| 498 | 501 | #### `hello_ack` | |
| 499 | 502 | ||
| 500 | 503 | ```json | |
| 501 | - | {"type":"hello_ack","protocol_version":1, | |
| 502 | - | "zot_version":"0.0.7","provider":"anthropic", | |
| 503 | - | "model":"claude-opus-4-7","cwd":"/Users/pat/Developer/zot", | |
| 504 | - | "extension_dir":"/Users/pat/Developer/zot/.zot/extensions/todos", | |
| 505 | - | "data_dir":"/Users/pat/Developer/zot/.zot/extensions/todos", | |
| 504 | + | {"type":"hello_ack","protocol_version":2, | |
| 505 | + | "zut_version":"0.0.7","provider":"anthropic", | |
| 506 | + | "model":"claude-opus-4-7","cwd":"/path/to/zut", | |
| 507 | + | "extension_dir":"/path/to/zut/.zut/extensions/todos", | |
| 508 | + | "data_dir":"/path/to/zut/.zut/extensions/todos", | |
| 506 | 509 | "session":{"id":"branch-1","parent_id":"root-1", | |
| 507 | - | "path":".../session.jsonl","cwd":"/Users/pat/Developer/zot", | |
| 510 | + | "path":".../session.jsonl","cwd":"/path/to/zut", | |
| 508 | 511 | "fork_point":4}} | |
| 509 | 512 | ``` | |
| 510 | 513 | ||
| 511 | - | Sent immediately after `hello`. Wait for this frame before sending | |
| 512 | - | registrations if they depend on host metadata. The extension can use | |
| 514 | + | Sent immediately after a matching `hello`. The protocol version is a | |
| 515 | + | major and must match the version sent by the extension. Wait for this frame | |
| 516 | + | before sending registrations if they depend on host metadata. The extension can use | |
| 513 | 517 | these fields to decide which commands to register (e.g. only register | |
| 514 | 518 | a Python tool on macOS, only register a model-specific shortcut for | |
| 515 | 519 | opus, etc.). `cwd` is the user's project directory; the extension | |
| @@ -562,7 +566,7 @@ Lifecycle notification for events the extension subscribed to via | |||
| 562 | 566 | ||
| 563 | 567 | #### `event_intercept` | |
| 564 | 568 | ||
| 565 | - | Sent when zot wants to give the extension a chance to block, modify, | |
| 569 | + | Sent when zut wants to give the extension a chance to block, modify, | |
| 566 | 570 | or annotate a lifecycle event before it happens. Reply with | |
| 567 | 571 | `event_intercept_response` within 5s; missing the deadline is | |
| 568 | 572 | treated as "allow". | |
| @@ -598,7 +602,7 @@ name (`up`, `down`, `left`, `right`, `enter`, `esc`, `tab`, `pageup`, | |||
| 598 | 602 | ||
| 599 | 603 | #### `panel_close` | |
| 600 | 604 | ||
| 601 | - | Sent when the user closes the focused panel from zot (for example with | |
| 605 | + | Sent when the user closes the focused panel from zut (for example with | |
| 602 | 606 | Esc or Ctrl+C). The extension should treat this as the panel lifetime | |
| 603 | 607 | ending and stop sending `panel_render` updates for that `panel_id`. | |
| 604 | 608 | ||
| @@ -608,29 +612,29 @@ ending and stop sending `panel_render` updates for that `panel_id`. | |||
| 608 | 612 | ||
| 609 | 613 | #### `shutdown` | |
| 610 | 614 | ||
| 611 | - | Sent during graceful zot exit (or `/reload-ext` once that lands). | |
| 615 | + | Sent during graceful zut exit (or `/reload-ext` once that lands). | |
| 612 | 616 | Reply with `shutdown_ack` and then exit. | |
| 613 | 617 | ||
| 614 | 618 | ## Managing extensions from the CLI | |
| 615 | 619 | ||
| 616 | 620 | ``` | |
| 617 | - | zot ext list list installed extensions and their state | |
| 618 | - | zot ext doctor diagnose load, registration, and conflict issues | |
| 619 | - | zot ext install <path|git-url> copy / clone into $ZOT_HOME/extensions/ | |
| 620 | - | zot ext install --build=go <local-path> build local Go source, then install | |
| 621 | - | zot ext remove <name> delete an extension directory | |
| 622 | - | zot ext enable <name> re-enable a disabled extension | |
| 623 | - | zot ext disable <name> disable without removing | |
| 624 | - | zot ext logs <name> [-f] cat / tail the extension's stderr | |
| 621 | + | zut ext list list installed extensions and their state | |
| 622 | + | zut ext doctor diagnose load, registration, and conflict issues | |
| 623 | + | zut ext install <path|git-url> copy / clone into $ZUT_HOME/extensions/ | |
| 624 | + | zut ext install --build=go <local-path> build local Go source, then install | |
| 625 | + | zut ext remove <name> delete an extension directory | |
| 626 | + | zut ext enable <name> re-enable a disabled extension | |
| 627 | + | zut ext disable <name> disable without removing | |
| 628 | + | zut ext logs <name> [-f] cat / tail the extension's stderr | |
| 625 | 629 | ``` | |
| 626 | 630 | ||
| 627 | - | `zot ext doctor` runs the same discovery path as zot startup, but reports | |
| 631 | + | `zut ext doctor` runs the same discovery path as zut startup, but reports | |
| 628 | 632 | what happened instead of changing the fail-soft runtime behavior. It shows | |
| 629 | 633 | manifest errors, disabled or shadowed extensions, subprocess load errors, | |
| 630 | 634 | ready/auto-ready status, registered commands/tools, registration conflicts, | |
| 631 | 635 | warnings, and each extension's stderr log path. | |
| 632 | 636 | ||
| 633 | - | `zot ext install <path>` does a recursive copy; `<git-url>` does a | |
| 637 | + | `zut ext install <path>` does a recursive copy; `<git-url>` does a | |
| 634 | 638 | shallow clone. Both validate that the destination contains an | |
| 635 | 639 | `extension.json` and roll back if not. No build runs unless the local install | |
| 636 | 640 | is invoked with the explicit `--build=go` option. That option currently supports | |
| @@ -639,17 +643,17 @@ only local Go source paths; clone remote source locally before building it. | |||
| 639 | 643 | ## Loading an extension for one run | |
| 640 | 644 | ||
| 641 | 645 | For iteration on a working copy, skip the install + reload cycle | |
| 642 | - | and load straight from disk for one zot session: | |
| 646 | + | and load straight from disk for one zut session: | |
| 643 | 647 | ||
| 644 | 648 | ``` | |
| 645 | - | zot --ext ./my-extension # short form: -e ./my-extension | |
| 646 | - | zot --ext ./a -e ./b # repeatable | |
| 649 | + | zut --ext ./my-extension # short form: -e ./my-extension | |
| 650 | + | zut --ext ./a -e ./b # repeatable | |
| 647 | 651 | ``` | |
| 648 | 652 | ||
| 649 | 653 | `--ext` paths take precedence over installed extensions of the same | |
| 650 | 654 | name, so you can shadow an installed copy with a work-in-progress | |
| 651 | 655 | version without uninstalling first. Nothing is copied or persisted; | |
| 652 | - | the extension dies with zot like any other subprocess. | |
| 656 | + | the extension dies with zut like any other subprocess. | |
| 653 | 657 | ||
| 654 | 658 | ## SDKs | |
| 655 | 659 | ||
| @@ -663,7 +667,7 @@ package main | |||
| 663 | 667 | ||
| 664 | 668 | import ( | |
| 665 | 669 | "encoding/json" | |
| 666 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 670 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 667 | 671 | ) | |
| 668 | 672 | ||
| 669 | 673 | func main() { | |
| @@ -697,10 +701,10 @@ func main() { | |||
| 697 | 701 | ``` | |
| 698 | 702 | ||
| 699 | 703 | Build with `go build -o hello .`, drop the binary + an `extension.json` | |
| 700 | - | into `$ZOT_HOME/extensions/hello/`. | |
| 704 | + | into `$ZUT_HOME/extensions/hello/`. | |
| 701 | 705 | ||
| 702 | 706 | `OnHello` is optional. Use it when configuration or registrations need | |
| 703 | - | host metadata such as `HostInfo.CWD`, `Provider`, `Model`, `ZotVersion`, | |
| 707 | + | host metadata such as `HostInfo.CWD`, `Provider`, `Model`, `ZutVersion`, | |
| 704 | 708 | `ExtensionDir`, or `DataDir`. The SDK sends `hello`, waits for | |
| 705 | 709 | `hello_ack`, runs `OnHello`, announces registrations, then sends `ready`. | |
| 706 | 710 | ||
| @@ -770,7 +774,7 @@ The `tasked-phases` example bundles its companion skill under | |||
| 770 | 774 | `extension.json`. Installing the extension therefore installs the workflow | |
| 771 | 775 | skill automatically. The standalone copy at `examples/skills/tasked-phases/` | |
| 772 | 776 | remains useful for project-local skill installation. Its state is restored per | |
| 773 | - | session branch when zot session persistence is enabled; the extension keeps a | |
| 777 | + | session branch when zut session persistence is enabled; the extension keeps a | |
| 774 | 778 | project-file fallback for hosts that do not persist sessions. | |
| 775 | 779 | ||
| 776 | 780 | ### Hot reload | |
| @@ -778,7 +782,7 @@ project-file fallback for hosts that do not persist sessions. | |||
| 778 | 782 | Type `/reload-ext` in the TUI to tear down every running extension | |
| 779 | 783 | subprocess, re-read the manifests from disk, and respawn the set. | |
| 780 | 784 | The agent's tool registry is rebuilt automatically, so freshly- | |
| 781 | - | registered extension tools become callable without restarting zot. | |
| 785 | + | registered extension tools become callable without restarting zut. | |
| 782 | 786 | Useful while developing an extension: edit, save, `/reload-ext`, | |
| 783 | 787 | done. Explicit `--ext` paths are remembered and reloaded alongside | |
| 784 | 788 | discovered extensions. The temporary reload status reports each load | |
| @@ -803,9 +807,9 @@ Extensions run with **the user's full filesystem and network | |||
| 803 | 807 | permissions**. Treat installing an extension the same as installing | |
| 804 | 808 | any other binary on your machine. | |
| 805 | 809 | ||
| 806 | - | `zot ext install <git-url>` clones from any URL you give it. There's | |
| 810 | + | `zut ext install <git-url>` clones from any URL you give it. There's | |
| 807 | 811 | no sandbox in v1; if you need isolation, install only extensions you | |
| 808 | - | trust or run zot under your platform's sandboxing tool (`bwrap` / | |
| 812 | + | trust or run zut under your platform's sandboxing tool (`bwrap` / | |
| 809 | 813 | `sandbox-exec` / AppContainer). | |
| 810 | 814 | ||
| 811 | 815 | ## Roadmap | |
| @@ -814,7 +818,7 @@ Phase 1 (shipped): | |||
| 814 | 818 | - [x] subprocess lifecycle + hello handshake | |
| 815 | 819 | - [x] `register_command` + `command_invoked` | |
| 816 | 820 | - [x] `notify` + `clear_notes` + structured terminal alerts | |
| 817 | - | - [x] `zot ext` CLI | |
| 821 | + | - [x] `zut ext` CLI | |
| 818 | 822 | ||
| 819 | 823 | Phase 2 (shipped): | |
| 820 | 824 | - [x] `register_tool` + `tool_call` + `tool_result` | |
| @@ -832,7 +836,7 @@ Phase 4 (shipped): | |||
| 832 | 836 | addition to `tool_call`) | |
| 833 | 837 | - [x] modify tool args mid-flight via `modified_args` | |
| 834 | 838 | - [x] rewrite user-visible assistant text via `replace_text` | |
| 835 | - | - [x] `/reload-ext` slash command (hot-reload without restarting zot) | |
| 839 | + | - [x] `/reload-ext` slash command (hot-reload without restarting zut) | |
| 836 | 840 | ||
| 837 | 841 | Phase 5 (shipped): | |
| 838 | 842 | - [x] session/branch identity in `hello_ack` and lifecycle events | |
M docs/lsp.md +8 -8
| @@ -1,18 +1,18 @@ | |||
| 1 | 1 | # LSP and linter support | |
| 2 | 2 | ||
| 3 | - | zot exposes a built-in `lsp` tool for code intelligence and diagnostics. It | |
| 3 | + | zut exposes a built-in `lsp` tool for code intelligence and diagnostics. It | |
| 4 | 4 | communicates with stdio language servers through JSON-RPC and can also run | |
| 5 | 5 | configured command-line linters without a shell. | |
| 6 | 6 | ||
| 7 | 7 | ## Configuration | |
| 8 | 8 | ||
| 9 | - | zot loads JSON configuration from the following layers, in increasing | |
| 9 | + | zut loads JSON configuration from the following layers, in increasing | |
| 10 | 10 | precedence: | |
| 11 | 11 | ||
| 12 | - | - `$ZOT_HOME/lsp.json` | |
| 12 | + | - `$ZUT_HOME/lsp.json` | |
| 13 | 13 | - `<cwd>/.pi/lsp.json` | |
| 14 | 14 | - `<cwd>/.omp/lsp.json` | |
| 15 | - | - `<cwd>/lsp.json`, `<cwd>/.lsp.json`, and `<cwd>/.zot/lsp.json` | |
| 15 | + | - `<cwd>/lsp.json`, `<cwd>/.lsp.json`, and `<cwd>/.zut/lsp.json` | |
| 16 | 16 | ||
| 17 | 17 | A file may put providers below `servers`, `lspServers`, or `linters`, use a | |
| 18 | 18 | flat provider map, or use the `providers` array: | |
| @@ -43,7 +43,7 @@ The `providers` array accepts built-in names and objects with `id`, `kind`, | |||
| 43 | 43 | Unknown provider metadata is ignored. Set `autoDetect` to `false` to | |
| 44 | 44 | keep only providers named or defined by the configuration file. | |
| 45 | 45 | ||
| 46 | - | Server overrides merge onto zot's built-in definitions. Built-ins cover common | |
| 46 | + | Server overrides merge onto zut's built-in definitions. Built-ins cover common | |
| 47 | 47 | Go, Rust, TypeScript/JavaScript, Python, C/C++, YAML, JSON, and shell servers, | |
| 48 | 48 | plus ESLint, golangci-lint, and Ruff CLI providers. A provider is selected | |
| 49 | 49 | when it is enabled and its file type/root markers match the workspace. Command | |
| @@ -125,14 +125,14 @@ diagnostics allows a later recurrence to be surfaced again. | |||
| 125 | 125 | ||
| 126 | 126 | The sub-agent setting does not grant access to a child whose profile or | |
| 127 | 127 | explicit tool list omits `lsp`. Both settings can also be controlled through | |
| 128 | - | `$ZOT_HOME/config.json`. | |
| 128 | + | `$ZUT_HOME/config.json`. | |
| 129 | 129 | ||
| 130 | 130 | ## Safety and lifecycle | |
| 131 | 131 | ||
| 132 | 132 | Language servers and linters run as local child processes. Project and | |
| 133 | 133 | global LSP configuration is executable local configuration: review configured | |
| 134 | 134 | commands, arguments, environment variables, and workspace-local binaries before | |
| 135 | - | using it. zot does not add a separate trust prompt or sandbox around those child | |
| 135 | + | using it. zut does not add a separate trust prompt or sandbox around those child | |
| 136 | 136 | processes; it does avoid shell expansion and keeps returned workspace edits | |
| 137 | 137 | inside the session workspace. The `lsp` tool never executes configured commands | |
| 138 | 138 | through a shell. Workspace edits returned by | |
| @@ -141,7 +141,7 @@ resource operations, validate UTF-16 LSP positions, and write through a | |||
| 141 | 141 | temporary file before replacement. Server-initiated `workspace/applyEdit` | |
| 142 | 142 | requests use the same checks. | |
| 143 | 143 | ||
| 144 | - | LSP clients are cached per workspace and provider for the lifetime of the zot | |
| 144 | + | LSP clients are cached per workspace and provider for the lifetime of the zut | |
| 145 | 145 | process. `reload` and process shutdown terminate cached children. Server | |
| 146 | 146 | stderr is kept out of model-facing tool results; diagnostics and protocol | |
| 147 | 147 | errors are returned through the established tool result channel. | |
M docs/providers.md +31 -31
| @@ -1,14 +1,14 @@ | |||
| 1 | - | # zot providers | |
| 1 | + | # zut providers | |
| 2 | 2 | ||
| 3 | - | zot ships with built-in providers and a model catalog. You can select models | |
| 4 | - | with `/model`, list them with `zot --list-models`, and add private models in | |
| 5 | - | `$ZOT_HOME/models.json`. | |
| 3 | + | zut ships with built-in providers and a model catalog. You can select models | |
| 4 | + | with `/model`, list them with `zut --list-models`, and add private models in | |
| 5 | + | `$ZUT_HOME/models.json`. | |
| 6 | 6 | ||
| 7 | 7 | ## Login methods | |
| 8 | 8 | ||
| 9 | 9 | Use `/login` in interactive mode. Type in either provider picker to filter the list by provider ID or display name. | |
| 10 | 10 | ||
| 11 | - | - `api key`: stores an API key in `$ZOT_HOME/auth.json` when the provider uses a normal key. | |
| 11 | + | - `api key`: stores an API key in `$ZUT_HOME/auth.json` when the provider uses a normal key. | |
| 12 | 12 | - `subscription`: stores OAuth credentials for subscription-backed providers. | |
| 13 | 13 | ||
| 14 | 14 | Use `/logout` to remove stored credentials. | |
| @@ -20,7 +20,7 @@ sessions. | |||
| 20 | 20 | ||
| 21 | 21 | ### Command-backed API keys | |
| 22 | 22 | ||
| 23 | - | A provider can obtain its API key from a password manager or another local program. Configure this directly in `$ZOT_HOME/auth.json`: | |
| 23 | + | A provider can obtain its API key from a password manager or another local program. Configure this directly in `$ZUT_HOME/auth.json`: | |
| 24 | 24 | ||
| 25 | 25 | ```json | |
| 26 | 26 | { | |
| @@ -49,9 +49,9 @@ Custom provider credentials use the same object under `additional_api_key_creds` | |||
| 49 | 49 | } | |
| 50 | 50 | ``` | |
| 51 | 51 | ||
| 52 | - | The program is started directly rather than through a shell. The timeout defaults to 120 seconds. It must write one non-empty line to stdout, with no more than 64 KiB of output. Trailing CR/LF characters are removed. Successful results stay only in process memory and are reused until zot exits. | |
| 52 | + | The program is started directly rather than through a shell. The timeout defaults to 120 seconds. It must write one non-empty line to stdout, with no more than 64 KiB of output. Trailing CR/LF characters are removed. Successful results stay only in process memory and are reused until zut exits. | |
| 53 | 53 | ||
| 54 | - | Command-backed credentials count as logged in without being executed. zot materializes one only when selecting that provider; background model discovery skips it. `/login` with a normal key replaces the command, while `/logout` clears it. Because `auth.json` can cause program execution, keep it user-writable only and do not use files from untrusted sources. | |
| 54 | + | Command-backed credentials count as logged in without being executed. zut materializes one only when selecting that provider; background model discovery skips it. `/login` with a normal key replaces the command, while `/logout` clears it. Because `auth.json` can cause program execution, keep it user-writable only and do not use files from untrusted sources. | |
| 55 | 55 | ||
| 56 | 56 | Setup-instruction providers: | |
| 57 | 57 | ||
| @@ -73,7 +73,7 @@ These providers support subscription login: | |||
| 73 | 73 | | xAI | SuperGrok or X Premium device-code login. The browser URL is prefilled with the device code. | | |
| 74 | 74 | | GitHub Copilot | GitHub Copilot token flow. | | |
| 75 | 75 | ||
| 76 | - | OAuth tokens are stored in `$ZOT_HOME/auth.json` and refreshed when refresh is | |
| 76 | + | OAuth tokens are stored in `$ZUT_HOME/auth.json` and refreshed when refresh is | |
| 77 | 77 | available. | |
| 78 | 78 | ||
| 79 | 79 | ## API-key providers | |
| @@ -119,7 +119,7 @@ show instructions and should be configured with environment variables. | |||
| 119 | 119 | ||
| 120 | 120 | OpenAI, OpenAI Responses, and OpenAI Codex requests can opt into OpenAI's | |
| 121 | 121 | Fast service tier from `/settings` by enabling **fast mode**. The setting is | |
| 122 | - | off by default and is stored as `fast_mode` in `$ZOT_HOME/config.json`. | |
| 122 | + | off by default and is stored as `fast_mode` in `$ZUT_HOME/config.json`. | |
| 123 | 123 | ||
| 124 | 124 | Fast mode is currently limited to those OpenAI provider IDs. Enabling it while | |
| 125 | 125 | using another provider returns an error instead of silently changing that | |
| @@ -129,7 +129,7 @@ Example: | |||
| 129 | 129 | ||
| 130 | 130 | ```bash | |
| 131 | 131 | export OPENROUTER_API_KEY=... | |
| 132 | - | zot --provider openrouter | |
| 132 | + | zut --provider openrouter | |
| 133 | 133 | ``` | |
| 134 | 134 | ||
| 135 | 135 | ## Local llama.cpp router | |
| @@ -143,7 +143,7 @@ brew install llama.cpp | |||
| 143 | 143 | # use `brew upgrade llama.cpp` for an existing installation | |
| 144 | 144 | ``` | |
| 145 | 145 | ||
| 146 | - | Start `llama-server` without `--model`, `-m`, or `-hf`. Those flags select one model and disable the router behavior zot expects. | |
| 146 | + | Start `llama-server` without `--model`, `-m`, or `-hf`. Those flags select one model and disable the router behavior zut expects. | |
| 147 | 147 | ||
| 148 | 148 | ```bash | |
| 149 | 149 | mkdir -p ~/llama-models | |
| @@ -160,7 +160,7 @@ llama-server \ | |||
| 160 | 160 | ||
| 161 | 161 | This configuration discovers GGUF files below `~/llama-models`, leaves model loading under explicit user control, enables chat templates, requests GPU offload, and sets a 32K context. Tune the GPU layers and context size for the available memory. | |
| 162 | 162 | ||
| 163 | - | Check the management API before opening zot: | |
| 163 | + | Check the management API before opening zut: | |
| 164 | 164 | ||
| 165 | 165 | ```bash | |
| 166 | 166 | curl http://127.0.0.1:8080/health | |
| @@ -169,7 +169,7 @@ curl http://127.0.0.1:8080/models | |||
| 169 | 169 | ||
| 170 | 170 | `/models` must return JSON with a `data` array. A 404 indicates the wrong URL, an older server build, or single-model mode. | |
| 171 | 171 | ||
| 172 | - | To save the connection, run `/login`, choose `api key`, and select `llama.cpp`. Enter `http://127.0.0.1:8080`, without `/v1`, followed by an optional bearer key. zot validates the URL and stores both values in `$ZOT_HOME/auth.json`. Environment variables override the saved connection: | |
| 172 | + | To save the connection, run `/login`, choose `api key`, and select `llama.cpp`. Enter `http://127.0.0.1:8080`, without `/v1`, followed by an optional bearer key. zut validates the URL and stores both values in `$ZUT_HOME/auth.json`. Environment variables override the saved connection: | |
| 173 | 173 | ||
| 174 | 174 | ```bash | |
| 175 | 175 | export LLAMA_BASE_URL=http://127.0.0.1:8080 | |
| @@ -188,7 +188,7 @@ Ollama downloads are stored in Ollama's internal layout and do not automatically | |||
| 188 | 188 | ||
| 189 | 189 | ### Amazon Bedrock | |
| 190 | 190 | ||
| 191 | - | Bedrock is configured with AWS credentials, not a generic zot API-key entry. | |
| 191 | + | Bedrock is configured with AWS credentials, not a generic zut API-key entry. | |
| 192 | 192 | Use one of these credential sources: | |
| 193 | 193 | ||
| 194 | 194 | ```bash | |
| @@ -214,13 +214,13 @@ Example: | |||
| 214 | 214 | ||
| 215 | 215 | ```bash | |
| 216 | 216 | AWS_BEARER_TOKEN_BEDROCK=bedrock-api-key-... AWS_REGION=us-east-1 \ | |
| 217 | - | zot --provider amazon-bedrock --model anthropic.claude-sonnet-4-5-20250929-v1:0 | |
| 217 | + | zut --provider amazon-bedrock --model anthropic.claude-sonnet-4-5-20250929-v1:0 | |
| 218 | 218 | ``` | |
| 219 | 219 | ||
| 220 | 220 | Some Bedrock models require regional inference-profile IDs for on-demand | |
| 221 | - | throughput, such as `us.` or `eu.` prefixed model IDs. zot rewrites known | |
| 221 | + | throughput, such as `us.` or `eu.` prefixed model IDs. zut rewrites known | |
| 222 | 222 | families automatically where possible. Claude Opus 5 uses | |
| 223 | - | `global.anthropic.claude-opus-5`; zot maps the bare foundation-model ID to that | |
| 223 | + | `global.anthropic.claude-opus-5`; zut maps the bare foundation-model ID to that | |
| 224 | 224 | global profile and supports its adaptive reasoning levels. Explicit profile IDs | |
| 225 | 225 | and ARNs are left unchanged. | |
| 226 | 226 | ||
| @@ -230,7 +230,7 @@ Vertex can use a Google API key when available: | |||
| 230 | 230 | ||
| 231 | 231 | ```bash | |
| 232 | 232 | export GOOGLE_CLOUD_API_KEY=... | |
| 233 | - | zot --provider google-vertex | |
| 233 | + | zut --provider google-vertex | |
| 234 | 234 | ``` | |
| 235 | 235 | ||
| 236 | 236 | For service-account or application-default credentials, set the standard | |
| @@ -244,7 +244,7 @@ Cloudflare AI Gateway needs a Cloudflare token plus account and gateway IDs: | |||
| 244 | 244 | export CLOUDFLARE_API_KEY=... | |
| 245 | 245 | export CLOUDFLARE_ACCOUNT_ID=... | |
| 246 | 246 | export CLOUDFLARE_GATEWAY_ID=... | |
| 247 | - | zot --provider cloudflare-ai-gateway | |
| 247 | + | zut --provider cloudflare-ai-gateway | |
| 248 | 248 | ``` | |
| 249 | 249 | ||
| 250 | 250 | ### Cloudflare Workers AI | |
| @@ -254,7 +254,7 @@ Workers AI needs a Cloudflare token and account ID: | |||
| 254 | 254 | ```bash | |
| 255 | 255 | export CLOUDFLARE_API_KEY=... | |
| 256 | 256 | export CLOUDFLARE_ACCOUNT_ID=... | |
| 257 | - | zot --provider cloudflare-workers-ai | |
| 257 | + | zut --provider cloudflare-workers-ai | |
| 258 | 258 | ``` | |
| 259 | 259 | ||
| 260 | 260 | ### Azure OpenAI Responses | |
| @@ -263,10 +263,10 @@ zot --provider cloudflare-workers-ai | |||
| 263 | 263 | export AZURE_OPENAI_API_KEY=... | |
| 264 | 264 | export AZURE_OPENAI_BASE_URL=https://your-resource.openai.azure.com | |
| 265 | 265 | export AZURE_OPENAI_API_VERSION=v1 # optional, v1 is the default | |
| 266 | - | zot --provider azure-openai-responses | |
| 266 | + | zut --provider azure-openai-responses | |
| 267 | 267 | ``` | |
| 268 | 268 | ||
| 269 | - | The provider uses Azure's Responses API. If deployment names differ from zot | |
| 269 | + | The provider uses Azure's Responses API. If deployment names differ from zut | |
| 270 | 270 | model IDs, map them without changing the catalog: | |
| 271 | 271 | ||
| 272 | 272 | ```bash | |
| @@ -275,8 +275,8 @@ export AZURE_OPENAI_DEPLOYMENT_NAME_MAP='gpt-5.6-luna=luna-preview,gpt-5.6-sol=s | |||
| 275 | 275 | ||
| 276 | 276 | ## Auth file | |
| 277 | 277 | ||
| 278 | - | Credentials are stored in `$ZOT_HOME/auth.json` with user-only permissions | |
| 279 | - | when zot creates the file. | |
| 278 | + | Credentials are stored in `$ZUT_HOME/auth.json` with user-only permissions | |
| 279 | + | when zut creates the file. | |
| 280 | 280 | ||
| 281 | 281 | Example: | |
| 282 | 282 | ||
| @@ -294,11 +294,11 @@ Example: | |||
| 294 | 294 | ||
| 295 | 295 | The top-level keys are used for providers with dedicated credential fields. | |
| 296 | 296 | Other API-key providers are stored under `additional_api_key_creds`. Prefer | |
| 297 | - | `/login` so zot writes the correct schema. | |
| 297 | + | `/login` so zut writes the correct schema. | |
| 298 | 298 | ||
| 299 | 299 | ## Custom providers and models | |
| 300 | 300 | ||
| 301 | - | Use `$ZOT_HOME/models.json` for private models, deployment aliases, local | |
| 301 | + | Use `$ZUT_HOME/models.json` for private models, deployment aliases, local | |
| 302 | 302 | servers, or OpenAI-compatible gateways that are not in the built-in catalog. | |
| 303 | 303 | User entries override built-in entries with the same provider and model ID, and | |
| 304 | 304 | adding a `models.json` no longer hides the built-in catalog: your entries are | |
| @@ -333,7 +333,7 @@ not expose a model-list endpoint, custom provider keys are accepted and stored | |||
| 333 | 333 | without a verification probe; an invalid key surfaces on the first model call. | |
| 334 | 334 | ||
| 335 | 335 | To retrieve this custom provider's key from a password manager, add a matching | |
| 336 | - | entry to `$ZOT_HOME/auth.json`: | |
| 336 | + | entry to `$ZUT_HOME/auth.json`: | |
| 337 | 337 | ||
| 338 | 338 | ```json | |
| 339 | 339 | { | |
| @@ -353,17 +353,17 @@ The provider IDs in `models.json` and `auth.json` must match. Then select the | |||
| 353 | 353 | custom model directly: | |
| 354 | 354 | ||
| 355 | 355 | ```sh | |
| 356 | - | zot --provider my-company --model company-llm-v2 | |
| 356 | + | zut --provider my-company --model company-llm-v2 | |
| 357 | 357 | ``` | |
| 358 | 358 | ||
| 359 | 359 | ## Credential resolution | |
| 360 | 360 | ||
| 361 | - | For each request, zot checks credentials in this order: | |
| 361 | + | For each request, zut checks credentials in this order: | |
| 362 | 362 | ||
| 363 | 363 | 1. Explicit CLI key, such as `--api-key`. | |
| 364 | 364 | 2. Provider-specific environment variables (including derived custom-provider | |
| 365 | 365 | variables such as `MY_COMPANY_API_KEY`). | |
| 366 | - | 3. `$ZOT_HOME/auth.json`, including custom provider keys saved by `/login`. | |
| 366 | + | 3. `$ZUT_HOME/auth.json`, including custom provider keys saved by `/login`. | |
| 367 | 367 | ||
| 368 | 368 | `models.json` itself never stores credentials; it only describes models and | |
| 369 | 369 | their endpoints. | |
M docs/rpc.md +12 -12
| @@ -1,41 +1,41 @@ | |||
| 1 | - | # zot RPC | |
| 1 | + | # zut RPC | |
| 2 | 2 | ||
| 3 | - | `zot rpc` runs the agent runtime as a subprocess that speaks newline-delimited JSON on stdin and stdout. Use it from any language that can spawn a process and read/write its pipes — Go, TypeScript, Python, Rust, shell, anything. | |
| 3 | + | `zut rpc` runs the agent runtime as a subprocess that speaks newline-delimited JSON on stdin and stdout. Use it from any language that can spawn a process and read/write its pipes — Go, TypeScript, Python, Rust, shell, anything. | |
| 4 | 4 | ||
| 5 | 5 | For a Go program embedding the runtime in-process, use the `packages/agent/sdk` SDK instead. The wire format below mirrors the SDK's types one-for-one so consumers can share parsing code. | |
| 6 | 6 | ||
| 7 | 7 | ## Quick start | |
| 8 | 8 | ||
| 9 | 9 | ```bash | |
| 10 | - | # spawn zot rpc; talk to it from a shell | |
| 10 | + | # spawn zut rpc; talk to it from a shell | |
| 11 | 11 | ( echo '{"id":"1","type":"prompt","message":"hello"}'; sleep 5 ) \ | |
| 12 | - | | zot rpc --provider anthropic | |
| 12 | + | | zut rpc --provider anthropic | |
| 13 | 13 | ``` | |
| 14 | 14 | ||
| 15 | 15 | You'll see one JSON object per line on stdout: a response acknowledging the prompt, a stream of events (`text_delta`, `tool_call`, `tool_result`, `usage`), then `done`. | |
| 16 | 16 | ||
| 17 | 17 | ## Process model | |
| 18 | 18 | ||
| 19 | - | - One `zot rpc` process serves **one cwd, one model, one session**. | |
| 19 | + | - One `zut rpc` process serves **one cwd, one model, one session**. | |
| 20 | 20 | - For multiple projects, spawn multiple processes. | |
| 21 | 21 | - Concurrency: at most one prompt or compact in flight at a time. A second one queues until the first finishes; aborting fires immediately. | |
| 22 | 22 | - The process exits when stdin closes. | |
| 23 | 23 | ||
| 24 | 24 | ## Flags | |
| 25 | 25 | ||
| 26 | - | `zot rpc` accepts the same flags as the other modes: `--provider`, `--model`, `--cwd`, `--api-key`, `--base-url`, `--system-prompt`, `--append-system-prompt`, `--reasoning`, `--max-steps`, `--no-tools`, `--tools`. Sessions are disabled by default in RPC mode — the embedding application owns persistence. | |
| 26 | + | `zut rpc` accepts the same flags as the other modes: `--provider`, `--model`, `--cwd`, `--api-key`, `--base-url`, `--system-prompt`, `--append-system-prompt`, `--reasoning`, `--max-steps`, `--no-tools`, `--tools`. Sessions are disabled by default in RPC mode — the embedding application owns persistence. | |
| 27 | 27 | ||
| 28 | - | RPC agents use the same resolved system-prompt policy as other modes. Ponytail coding mode is enabled when `ponytail_enabled` is missing or `true` in `$ZOT_HOME/config.json`, and disabled when it is `false`. A custom `--system-prompt` replaces the built-in identity, while enabled append addenda remain present. | |
| 28 | + | RPC agents use the same resolved system-prompt policy as other modes. Ponytail coding mode is enabled when `ponytail_enabled` is missing or `true` in `$ZUT_HOME/config.json`, and disabled when it is `false`. A custom `--system-prompt` replaces the built-in identity, while enabled append addenda remain present. | |
| 29 | 29 | ||
| 30 | 30 | ## Auth | |
| 31 | 31 | ||
| 32 | - | If the environment variable `ZOTCORE_RPC_TOKEN` is set on the spawned process, the first line on stdin **must** be a `hello` command containing the matching token: | |
| 32 | + | If the environment variable `ZUTCORE_RPC_TOKEN` is set on the spawned process, the first line on stdin **must** be a `hello` command containing the matching token: | |
| 33 | 33 | ||
| 34 | 34 | ```json | |
| 35 | 35 | {"id":"0","type":"hello","token":"shared-secret"} | |
| 36 | 36 | ``` | |
| 37 | 37 | ||
| 38 | - | If absent or wrong, the response carries `success:false` and the process exits. Without `ZOTCORE_RPC_TOKEN` set, no auth is required (the spawning process is implicitly trusted; if it can spawn `zot` it can also read your `auth.json` directly). | |
| 38 | + | If absent or wrong, the response carries `success:false` and the process exits. Without `ZUTCORE_RPC_TOKEN` set, no auth is required (the spawning process is implicitly trusted; if it can spawn `zut` it can also read your `auth.json` directly). | |
| 39 | 39 | ||
| 40 | 40 | ## Wire format | |
| 41 | 41 | ||
| @@ -66,7 +66,7 @@ Response: | |||
| 66 | 66 | "data":{"protocol_version":1,"version":"0.0.4","provider":"anthropic","model":"claude-opus-4-5"}} | |
| 67 | 67 | ``` | |
| 68 | 68 | ||
| 69 | - | Required as the first message when `ZOTCORE_RPC_TOKEN` is set; optional otherwise. | |
| 69 | + | Required as the first message when `ZUTCORE_RPC_TOKEN` is set; optional otherwise. | |
| 70 | 70 | ||
| 71 | 71 | ### `prompt` | |
| 72 | 72 | ||
| @@ -120,7 +120,7 @@ Response data: | |||
| 120 | 120 | { | |
| 121 | 121 | "provider": "anthropic", | |
| 122 | 122 | "model": "claude-opus-4-5", | |
| 123 | - | "cwd": "/Users/pat/Developer/zot", | |
| 123 | + | "cwd": "/path/to/zut", | |
| 124 | 124 | "message_count": 12, | |
| 125 | 125 | "busy": false, | |
| 126 | 126 | "usage": {"input": 1234, "output": 567, "reasoning": 123, "cache_read": 890, "cache_write": 0, "cost_usd": 0.0123} | |
| @@ -153,7 +153,7 @@ Switch model within the same provider. | |||
| 153 | 153 | {"id":"7","type":"set_model","model":"claude-sonnet-4-5"} | |
| 154 | 154 | ``` | |
| 155 | 155 | ||
| 156 | - | Cross-provider swaps require relaunching `zot rpc` with the new `--provider`. | |
| 156 | + | Cross-provider swaps require relaunching `zut rpc` with the new `--provider`. | |
| 157 | 157 | ||
| 158 | 158 | ### `set_reasoning` | |
| 159 | 159 | ||
M docs/skills.md +16 -16
| @@ -1,8 +1,8 @@ | |||
| 1 | - | # zot skills | |
| 1 | + | # zut skills | |
| 2 | 2 | ||
| 3 | 3 | A skill is a reusable instruction set written as a single | |
| 4 | 4 | `SKILL.md` file with a YAML frontmatter header. Unless a skill disables | |
| 5 | - | model invocation, zot discovers it at startup and surfaces it to the model | |
| 5 | + | model invocation, zut discovers it at startup and surfaces it to the model | |
| 6 | 6 | in two ways: | |
| 7 | 7 | ||
| 8 | 8 | 1. The system prompt gains a short manifest: | |
| @@ -49,22 +49,22 @@ There's no template engine; the model sees what you write. | |||
| 49 | 49 | ||
| 50 | 50 | ## Discovery | |
| 51 | 51 | ||
| 52 | - | zot looks in these directories, in priority order, and registers the | |
| 52 | + | zut looks in these directories, in priority order, and registers the | |
| 53 | 53 | first `SKILL.md` it finds for each unique name: | |
| 54 | 54 | ||
| 55 | 55 | | location | scope | | |
| 56 | 56 | |---|---| | |
| 57 | - | | `./.zot/skills/<name>/SKILL.md` | project (native) | | |
| 58 | - | | `$ZOT_HOME/skills/<name>/SKILL.md` | global (native) | | |
| 57 | + | | `./.zut/skills/<name>/SKILL.md` | project (native) | | |
| 58 | + | | `$ZUT_HOME/skills/<name>/SKILL.md` | global (native) | | |
| 59 | 59 | | `./.claude/skills/<name>/SKILL.md` | project (claude-compat) | | |
| 60 | 60 | | `~/.claude/skills/<name>/SKILL.md` | global (claude-compat) | | |
| 61 | 61 | | `./.agents/skills/<name>/SKILL.md` | project (agent-compat) | | |
| 62 | 62 | | `~/.agents/skills/<name>/SKILL.md` | global (agent-compat) | | |
| 63 | 63 | ||
| 64 | 64 | The compat paths are deliberate: a `SKILL.md` written for an existing | |
| 65 | - | skill ecosystem works in zot unchanged. Drop your existing | |
| 65 | + | skill ecosystem works in zut unchanged. Drop your existing | |
| 66 | 66 | `.claude/skills/` or `.agents/skills/` directories into a project and | |
| 67 | - | zot will pick them up. User skill roots are scanned recursively, so | |
| 67 | + | zut will pick them up. User skill roots are scanned recursively, so | |
| 68 | 68 | nested files such as `~/.agents/skills/systems-backend/subskills/golang-patterns/SKILL.md` | |
| 69 | 69 | are discovered too. The frontmatter `name` is the canonical identifier; | |
| 70 | 70 | for nested user skills, the slash-separated directory path relative to the | |
| @@ -74,20 +74,20 @@ Enabled extensions may bundle skills by declaring relative directories in | |||
| 74 | 74 | `extension.json`, for example `"skills": ["skills"]`, with files at | |
| 75 | 75 | `skills/<name>/SKILL.md`. A declared directory may also contain a single | |
| 76 | 76 | `SKILL.md` directly; its directory name supplies the fallback skill name and | |
| 77 | - | child directories are not scanned. Bundled skills are copied by `zot ext | |
| 77 | + | child directories are not scanned. Bundled skills are copied by `zut ext | |
| 78 | 78 | install` and loaded automatically. User/project skills take precedence over | |
| 79 | 79 | bundled skills; bundled skills take precedence over embedded built-ins. When | |
| 80 | 80 | two extensions provide the same skill name, the first declaration wins. The | |
| 81 | 81 | host rejects manifest paths that escape the extension directory, including | |
| 82 | 82 | symlinked skill files that resolve outside it. | |
| 83 | 83 | ||
| 84 | - | When `XDG_STATE_HOME` is set on any platform, `$ZOT_HOME` defaults to | |
| 85 | - | `$XDG_STATE_HOME/zot`. Otherwise it defaults to `~/Library/Application Support/zot/` | |
| 86 | - | on macOS, `~/.local/state/zot` on Linux, or `%LOCALAPPDATA%\zot` on Windows. | |
| 84 | + | When `XDG_STATE_HOME` is set on any platform, `$ZUT_HOME` defaults to | |
| 85 | + | `$XDG_STATE_HOME/zut`. Otherwise it defaults to `~/Library/Application Support/zut/` | |
| 86 | + | on macOS, `~/.local/state/zut` on Linux, or `%LOCALAPPDATA%\zut` on Windows. | |
| 87 | 87 | ||
| 88 | 88 | ## Inspecting installed skills | |
| 89 | 89 | ||
| 90 | - | In zot, run `/skills`. A picker lists every discovered skill with its | |
| 90 | + | In zut, run `/skills`. A picker lists every discovered skill with its | |
| 91 | 91 | description and source path. Press enter on a row to view the full | |
| 92 | 92 | body inline. Press esc to go back. | |
| 93 | 93 | ||
| @@ -104,7 +104,7 @@ To force a specific skill, invoke it as a slash command. Typing `/skill:` opens | |||
| 104 | 104 | /skill:code-review focus on security issues | |
| 105 | 105 | ``` | |
| 106 | 106 | ||
| 107 | - | zot expands the command into a user message containing the complete skill | |
| 107 | + | zut expands the command into a user message containing the complete skill | |
| 108 | 108 | body, its directory for resolving relative references, and any text following | |
| 109 | 109 | the command as the request. This bypasses model-side skill selection. | |
| 110 | 110 | ||
| @@ -136,11 +136,11 @@ See `examples/skills/` for starter skills: | |||
| 136 | 136 | ||
| 137 | 137 | ## Comparison to other discovery layouts | |
| 138 | 138 | ||
| 139 | - | | ecosystem | path | zot reads it? | | |
| 139 | + | | ecosystem | path | zut reads it? | | |
| 140 | 140 | |---|---|---| | |
| 141 | - | | (native) | `.zot/skills/<name>/SKILL.md` | yes | | |
| 141 | + | | (native) | `.zut/skills/<name>/SKILL.md` | yes | | |
| 142 | 142 | | (claude-style) | `.claude/skills/<name>/SKILL.md` | yes | | |
| 143 | 143 | | (agent-style) | `.agents/skills/<name>/SKILL.md` | yes | | |
| 144 | 144 | ||
| 145 | 145 | Cross-pollination is intentional: pick whichever convention you're | |
| 146 | - | already using and zot tags along. | |
| 146 | + | already using and zut tags along. | |
M docs/subagents.md +8 -8
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | # Named subagent profiles | |
| 2 | 2 | ||
| 3 | - | zot can discover reusable named subagent definitions from markdown files with a frontmatter block. This is a common agent-profile layout and is not tied to a particular host application. | |
| 3 | + | zut can discover reusable named subagent definitions from markdown files with a frontmatter block. This is a common agent-profile layout and is not tied to a particular host application. | |
| 4 | 4 | ||
| 5 | 5 | ## Discovery | |
| 6 | 6 | ||
| @@ -10,12 +10,12 @@ The default directory is: | |||
| 10 | 10 | ~/.agents/agents/*.md | |
| 11 | 11 | ``` | |
| 12 | 12 | ||
| 13 | - | zot also reads these optional locations: | |
| 13 | + | zut also reads these optional locations: | |
| 14 | 14 | ||
| 15 | - | - directories listed in `ZOT_AGENT_PROFILES` (use the platform path-list separator) | |
| 15 | + | - directories listed in `ZUT_AGENT_PROFILES` (use the platform path-list separator) | |
| 16 | 16 | - `~/.pi/agent/agents/*.md` as a compatibility fallback | |
| 17 | 17 | ||
| 18 | - | Profiles are read-only inputs. zot does not execute files from these directories. Project-local profile directories are not scanned automatically; use `ZOT_AGENT_PROFILES` when a project intentionally opts into an additional profile directory. | |
| 18 | + | Profiles are read-only inputs. zut does not execute files from these directories. Project-local profile directories are not scanned automatically; use `ZUT_AGENT_PROFILES` when a project intentionally opts into an additional profile directory. | |
| 19 | 19 | ||
| 20 | 20 | ## File format | |
| 21 | 21 | ||
| @@ -41,7 +41,7 @@ Supported metadata: | |||
| 41 | 41 | |---|---| | |
| 42 | 42 | | `name` | Name passed to `subagent_spawn`'s `agent` field. Falls back to the filename. | | |
| 43 | 43 | | `description` | Short description shown to the main agent. | | |
| 44 | - | | `tools` | Comma-separated or list-form tool names. zot enforces its built-in `read`, `write`, `edit`, `bash`, and `lsp` registry; the conditional `skill` tool is available when skills are enabled. Unknown names do not grant capabilities. | | |
| 44 | + | | `tools` | Comma-separated or list-form tool names. zut enforces its built-in `read`, `write`, `edit`, `bash`, and `lsp` registry; the conditional `skill` tool is available when skills are enabled. Unknown names do not grant capabilities. | | |
| 45 | 45 | | `model` | Optional model ID. A qualified value such as `openai-codex/gpt-5.6-luna` selects both provider and model. | | |
| 46 | 46 | | `provider` | Optional separate provider ID for a model without a provider prefix. | | |
| 47 | 47 | | `thinking` / `reasoning` | Optional reasoning level: `off`, `minimum`, `low`, `medium`, `high`, `xhigh`, or `max`. | | |
| @@ -50,7 +50,7 @@ Supported metadata: | |||
| 50 | 50 | | `inheritSkills` | Set to `false` to omit skill discovery and the conditional `skill` loader from the child; `--no-skill` has the same effect for a run. | | |
| 51 | 51 | | `fastMode` | Optional fast-mode restriction. Omit it to inherit the host setting; `false` disables fast mode for this profile; `true` only permits fast mode when the host setting is enabled. | | |
| 52 | 52 | ||
| 53 | - | Other frontmatter from another agent host is ignored when zot does not have an equivalent setting. Recursive child spawning is disabled in v1; a worker cannot invoke `subagent_spawn` to create descendants. | |
| 53 | + | Other frontmatter from another agent host is ignored when zut does not have an equivalent setting. Recursive child spawning is disabled in v1; a worker cannot invoke `subagent_spawn` to create descendants. | |
| 54 | 54 | ||
| 55 | 55 | ## Selecting a profile | |
| 56 | 56 | ||
| @@ -84,7 +84,7 @@ The interactive command also supports the same selection explicitly: | |||
| 84 | 84 | /subagents new --agent implementer --reasoning high Implement the parser change | |
| 85 | 85 | ``` | |
| 86 | 86 | ||
| 87 | - | Shared mode preserves the historical host working directory. For parallel coding, pass `isolation:"worktree"` to `subagent_spawn`; zot creates a detached Git worktree, captures changed files and a patch, and never merges automatically. Named profiles change the child's instructions and configuration; they are not a security sandbox. A profile's `systemPromptMode` controls its own body relative to the built-in identity; global append addenda, including enabled Ponytail coding guidance, remain present. Child credentials are transferred over stdin rather than argv or persisted metadata, and the active provider endpoint/TLS setting is inherited only when the child uses that provider. Fast mode is inherited by default. A profile with `fastMode: false` opts out, while `fastMode: true` cannot enable fast mode when the host setting is off. Non-OpenAI child providers return an unsupported-provider error instead of silently ignoring an enabled setting. | |
| 87 | + | Shared mode preserves the historical host working directory. For parallel coding, pass `isolation:"worktree"` to `subagent_spawn`; zut creates a detached Git worktree, captures changed files and a patch, and never merges automatically. Named profiles change the child's instructions and configuration; they are not a security sandbox. A profile's `systemPromptMode` controls its own body relative to the built-in identity; global append addenda, including enabled Ponytail coding guidance, remain present. Child credentials are transferred over stdin rather than argv or persisted metadata, and the active provider endpoint/TLS setting is inherited only when the child uses that provider. Fast mode is inherited by default. A profile with `fastMode: false` opts out, while `fastMode: true` cannot enable fast mode when the host setting is off. Non-OpenAI child providers return an unsupported-provider error instead of silently ignoring an enabled setting. | |
| 88 | 88 | ||
| 89 | 89 | ## Lifecycle, results, and recovery | |
| 90 | 90 | ||
| @@ -107,4 +107,4 @@ When auto-subagents is enabled, the model also receives the read-only `subagent_ | |||
| 107 | 107 | ||
| 108 | 108 | ## Resource policy | |
| 109 | 109 | ||
| 110 | - | The persisted `subagents` config object supports `max_concurrent`, `max_concurrent_per_parent`, `max_total_spawned`, `queue_timeout`, `default_timeout`, `max_turns`, output caps, allowed tools/roots, heartbeat and idle timeouts. A missing or non-positive configured `max_turns` uses the default ceiling of 3. The `max_turns` field in `subagent_spawn` is optional: omit it to use the policy ceiling, or provide a value from 1 through the configured maximum. Limits apply to slash commands, `subagent_spawn`, and batch operations. A child cannot create descendants in v1. Per-agent timeouts are retained across reload/resume. Packaged `zot run` agents keep their declared capability ceiling by disabling subagent delegation; profiles are not a substitute for that permission boundary. | |
| 110 | + | The persisted `subagents` config object supports `max_concurrent`, `max_concurrent_per_parent`, `max_total_spawned`, `queue_timeout`, `default_timeout`, `max_turns`, output caps, allowed tools/roots, heartbeat and idle timeouts. A missing or non-positive configured `max_turns` uses the default ceiling of 3. The `max_turns` field in `subagent_spawn` is optional: omit it to use the policy ceiling, or provide a value from 1 through the configured maximum. Limits apply to slash commands, `subagent_spawn`, and batch operations. A child cannot create descendants in v1. Per-agent timeouts are retained across reload/resume. Packaged `zut run` agents keep their declared capability ceiling by disabling subagent delegation; profiles are not a substitute for that permission boundary. | |
M docs/themes.md +28 -28
| @@ -1,9 +1,9 @@ | |||
| 1 | - | # zot themes | |
| 1 | + | # zut themes | |
| 2 | 2 | ||
| 3 | - | zot themes are JSON files that override any subset of the built-in | |
| 3 | + | zut themes are JSON files that override any subset of the built-in | |
| 4 | 4 | light/dark TUI theme. Nothing is required: a theme can change one | |
| 5 | 5 | color, only the spinner, only syntax highlighting, or all of them. | |
| 6 | - | Missing values inherit from zot's built-in default for the detected | |
| 6 | + | Missing values inherit from zut's built-in default for the detected | |
| 7 | 7 | terminal background. | |
| 8 | 8 | ||
| 9 | 9 | ## Where themes live | |
| @@ -11,32 +11,32 @@ terminal background. | |||
| 11 | 11 | User themes are discovered from: | |
| 12 | 12 | ||
| 13 | 13 | ```text | |
| 14 | - | $ZOT_HOME/themes/*.json | |
| 14 | + | $ZUT_HOME/themes/*.json | |
| 15 | 15 | ``` | |
| 16 | 16 | ||
| 17 | 17 | Open `/settings` and choose **color theme** to switch. Changes are | |
| 18 | - | saved in `$ZOT_HOME/config.json` and apply immediately. If the selected | |
| 19 | - | file is deleted later, zot resets the setting to the built-in auto | |
| 18 | + | saved in `$ZUT_HOME/config.json` and apply immediately. If the selected | |
| 19 | + | file is deleted later, zut resets the setting to the built-in auto | |
| 20 | 20 | (default) theme. | |
| 21 | 21 | ||
| 22 | 22 | The built-in choices are: | |
| 23 | 23 | ||
| 24 | - | - **auto** — detect the terminal background and use zot's built-in dark or | |
| 24 | + | - **auto** — detect the terminal background and use zut's built-in dark or | |
| 25 | 25 | light palette. This remains the default. | |
| 26 | 26 | - **inherited (from terminal)** — use the terminal's reported foreground, | |
| 27 | 27 | background, and ANSI palette. When `COLORTERM=truecolor`/`24bit` or | |
| 28 | - | `TERM` ends in `-direct`, zot emits truecolor; otherwise it quantizes | |
| 28 | + | `TERM` ends in `-direct`, zut emits truecolor; otherwise it quantizes | |
| 29 | 29 | derived colors to the xterm-256 palette as a best effort. Terminals that | |
| 30 | - | do not answer OSC color queries fall back to zot's built-in palette while | |
| 30 | + | do not answer OSC color queries fall back to zut's built-in palette while | |
| 31 | 31 | still using the advertised color depth. | |
| 32 | - | - **dark** / **light** — use zot's fixed built-in palettes. | |
| 32 | + | - **dark** / **light** — use zut's fixed built-in palettes. | |
| 33 | 33 | ||
| 34 | 34 | The inherited terminal snapshot is captured at startup and reused when the | |
| 35 | - | setting is changed live; restart zot to pick up terminal palette changes. | |
| 35 | + | setting is changed live; restart zut to pick up terminal palette changes. | |
| 36 | 36 | ||
| 37 | 37 | All theme color roles use the same terminal-color model. JSON themes may use | |
| 38 | 38 | xterm-256 indexes, ANSI SGR colors, or RGB values for any role. On terminals | |
| 39 | - | that advertise truecolor, zot emits RGB values directly and expands indexed | |
| 39 | + | that advertise truecolor, zut emits RGB values directly and expands indexed | |
| 40 | 40 | values through the xterm palette; otherwise RGB values fall back to the | |
| 41 | 41 | nearest xterm-256 color while indexed values remain indexed. ANSI values keep | |
| 42 | 42 | their SGR semantics, with standard foreground/background ranges normalized to | |
| @@ -52,13 +52,13 @@ Theme files bundled with extensions are discovered in-place from loaded | |||
| 52 | 52 | extension directories: | |
| 53 | 53 | ||
| 54 | 54 | ```text | |
| 55 | - | $ZOT_HOME/extensions/<extension>/theme.json | |
| 56 | - | $ZOT_HOME/extensions/<extension>/themes/theme.json | |
| 57 | - | <project>/.zot/extensions/<extension>/theme.json | |
| 58 | - | <project>/.zot/extensions/<extension>/themes/theme.json | |
| 55 | + | $ZUT_HOME/extensions/<extension>/theme.json | |
| 56 | + | $ZUT_HOME/extensions/<extension>/themes/theme.json | |
| 57 | + | <project>/.zut/extensions/<extension>/theme.json | |
| 58 | + | <project>/.zut/extensions/<extension>/themes/theme.json | |
| 59 | 59 | ``` | |
| 60 | 60 | ||
| 61 | - | zot does **not** copy extension themes into `$ZOT_HOME/themes`; extension | |
| 61 | + | zut does **not** copy extension themes into `$ZUT_HOME/themes`; extension | |
| 62 | 62 | owned themes stay in the extension directory. The settings picker shows | |
| 63 | 63 | source info such as `from extension my-theme-extension`. | |
| 64 | 64 | ||
| @@ -71,7 +71,7 @@ Metadata only: | |||
| 71 | 71 | ```json | |
| 72 | 72 | { | |
| 73 | 73 | "name": "my-theme", | |
| 74 | - | "description": "Metadata only; all visuals inherit zot defaults." | |
| 74 | + | "description": "Metadata only; all visuals inherit zut defaults." | |
| 75 | 75 | } | |
| 76 | 76 | ``` | |
| 77 | 77 | ||
| @@ -111,13 +111,13 @@ Spinner-only: | |||
| 111 | 111 | ``` | |
| 112 | 112 | ||
| 113 | 113 | Dark-only themes still work on light terminals. If `colors.light` is | |
| 114 | - | missing, zot applies `colors.dark` overrides on top of the built-in | |
| 114 | + | missing, zut applies `colors.dark` overrides on top of the built-in | |
| 115 | 115 | light default. The inverse also works. | |
| 116 | 116 | ||
| 117 | 117 | ```json | |
| 118 | 118 | { | |
| 119 | 119 | "name": "custom-spinner", | |
| 120 | - | "description": "An alternative spinner for zot that only displays a single spinner text.", | |
| 120 | + | "description": "An alternative spinner for zut that only displays a single spinner text.", | |
| 121 | 121 | "colors": { | |
| 122 | 122 | "dark": { | |
| 123 | 123 | "spinner_frames": ["◢", "◣", "◤", "◥"], | |
| @@ -137,7 +137,7 @@ All fields are optional. | |||
| 137 | 137 | "name": "my-theme", | |
| 138 | 138 | "description": "Shown in /settings → color theme.", | |
| 139 | 139 | "color_descriptions": { | |
| 140 | - | "accent": "Optional documentation for humans. zot ignores this object." | |
| 140 | + | "accent": "Optional documentation for humans. zut ignores this object." | |
| 141 | 141 | }, | |
| 142 | 142 | "colors": { | |
| 143 | 143 | "dark": { | |
| @@ -225,11 +225,11 @@ selected surfaces from the terminal foreground/background. | |||
| 225 | 225 | - `fg` — default foreground text. | |
| 226 | 226 | - `muted` — secondary text, dividers, gutters, inactive hints. | |
| 227 | 227 | - `accent` — prompt bar, bullets, links, headings, active markers. | |
| 228 | - | - `background` — optional full-row TUI background. If missing, zot uses the terminal's existing background. Experimental: terminal background colors can vary by emulator and scrollback behavior; for the most reliable result, change your terminal background color in your terminal settings instead. | |
| 228 | + | - `background` — optional full-row TUI background. If missing, zut uses the terminal's existing background. Experimental: terminal background colors can vary by emulator and scrollback behavior; for the most reliable result, change your terminal background color in your terminal settings instead. | |
| 229 | 229 | - `user` — user role label color; mostly compatibility. | |
| 230 | 230 | - `user_bubble_bg` — background behind user message rows. | |
| 231 | 231 | - `user_bubble_fg` — foreground inside user message rows. | |
| 232 | - | - `assistant` — assistant/zot accent and spinner text. | |
| 232 | + | - `assistant` — assistant/zut accent and spinner text. | |
| 233 | 233 | - `tool` — tool names, success marks, diff additions. | |
| 234 | 234 | - `tool_out` — plain tool-output text. | |
| 235 | 235 | - `error` — errors, refused calls, diff deletions. | |
| @@ -249,7 +249,7 @@ Spinner settings can appear at top level, under `colors`, or under | |||
| 249 | 249 | ||
| 250 | 250 | - `spinner_frames` — list of frame strings. Single-cell glyphs keep | |
| 251 | 251 | status-bar alignment clean. | |
| 252 | - | - `spinner_messages` — list of messages; zot picks one per turn. | |
| 252 | + | - `spinner_messages` — list of messages; zut picks one per turn. | |
| 253 | 253 | - `spinner_interval_ms` — frame interval in milliseconds. Missing or | |
| 254 | 254 | invalid falls back to 80ms. | |
| 255 | 255 | ||
| @@ -292,7 +292,7 @@ subprocess, or executable is required when the extension contains a | |||
| 292 | 292 | valid theme file. | |
| 293 | 293 | ||
| 294 | 294 | ```text | |
| 295 | - | $ZOT_HOME/extensions/my-theme-extension/ | |
| 295 | + | $ZUT_HOME/extensions/my-theme-extension/ | |
| 296 | 296 | ├── extension.json | |
| 297 | 297 | └── theme.json | |
| 298 | 298 | ``` | |
| @@ -303,17 +303,17 @@ $ZOT_HOME/extensions/my-theme-extension/ | |||
| 303 | 303 | { | |
| 304 | 304 | "name": "my-theme-extension", | |
| 305 | 305 | "version": "1.0.0", | |
| 306 | - | "description": "Ships a zot color theme", | |
| 306 | + | "description": "Ships a zut color theme", | |
| 307 | 307 | "enabled": true | |
| 308 | 308 | } | |
| 309 | 309 | ``` | |
| 310 | 310 | ||
| 311 | 311 | No `exec` is needed when `theme.json` or `themes/theme.json` exists. | |
| 312 | - | If `exec` is present, zot treats it as a normal extension too. | |
| 312 | + | If `exec` is present, zut treats it as a normal extension too. | |
| 313 | 313 | ||
| 314 | 314 | ## Validate | |
| 315 | 315 | ||
| 316 | - | zot theme files are plain JSON, not JSONC. Validate before installing: | |
| 316 | + | zut theme files are plain JSON, not JSONC. Validate before installing: | |
| 317 | 317 | ||
| 318 | 318 | ```bash | |
| 319 | 319 | python3 -m json.tool theme.json >/dev/null | |
R docs/zotfiles.md → docs/zutfiles.md +75 -75 renamed, 85% similar
| @@ -1,8 +1,8 @@ | |||
| 1 | - | # zotfile agents | |
| 1 | + | # zutfile agents | |
| 2 | 2 | ||
| 3 | - | A zotfile packages an agent's behavior into one portable `.zot` file. It can contain the agent's instructions, reusable skills, static assets, and metadata describing the runtime, model, operating-system, binary, and tool permissions it needs. | |
| 3 | + | A zutfile packages an agent's behavior into one portable `.zut` file. It can contain the agent's instructions, reusable skills, static assets, and metadata describing the runtime, model, operating-system, binary, and tool permissions it needs. | |
| 4 | 4 | ||
| 5 | - | The current implementation supports creating, inspecting, verifying, and running local directories and `.zot` archives. It can also run an agent repository or directory directly from any public GitHub repository without keeping a clone. zot has no built-in owner or official collection. Indexed registry distribution, installation, signatures, bundled executable extensions, network permissions, and environment permissions are not implemented yet. | |
| 5 | + | The current implementation supports creating, inspecting, verifying, and running local directories and `.zut` archives. It can also run an agent repository or directory directly from any public GitHub repository without keeping a clone. zut has no built-in owner or official collection. Indexed registry distribution, installation, signatures, bundled executable extensions, network permissions, and environment permissions are not implemented yet. | |
| 6 | 6 | ||
| 7 | 7 | ## Quick start | |
| 8 | 8 | ||
| @@ -20,12 +20,12 @@ Add a minimal `manifest.json`: | |||
| 20 | 20 | ||
| 21 | 21 | ```json | |
| 22 | 22 | { | |
| 23 | - | "zotfile": 1, | |
| 23 | + | "zutfile": 1, | |
| 24 | 24 | "name": "code-reviewer", | |
| 25 | 25 | "version": "0.1.0", | |
| 26 | 26 | "description": "Reviews a repository and reports actionable findings.", | |
| 27 | 27 | "runtime": { | |
| 28 | - | "min_zot": "0.2.76" | |
| 28 | + | "min_zut": "0.2.76" | |
| 29 | 29 | }, | |
| 30 | 30 | "model": { | |
| 31 | 31 | "requires": ["tools"], | |
| @@ -65,20 +65,20 @@ summarize files that have no findings. | |||
| 65 | 65 | Test the directory directly during development: | |
| 66 | 66 | ||
| 67 | 67 | ```bash | |
| 68 | - | zot inspect ./reviewer | |
| 69 | - | zot run ./reviewer | |
| 70 | - | zot run ./reviewer "Review the authentication package" | |
| 68 | + | zut inspect ./reviewer | |
| 69 | + | zut run ./reviewer | |
| 70 | + | zut run ./reviewer "Review the authentication package" | |
| 71 | 71 | ``` | |
| 72 | 72 | ||
| 73 | 73 | Package it when ready: | |
| 74 | 74 | ||
| 75 | 75 | ```bash | |
| 76 | - | zot pack ./reviewer | |
| 77 | - | # wrote code-reviewer.zot | |
| 76 | + | zut pack ./reviewer | |
| 77 | + | # wrote code-reviewer.zut | |
| 78 | 78 | # digest sha256:... | |
| 79 | 79 | ||
| 80 | - | zot verify ./code-reviewer.zot | |
| 81 | - | zot run ./code-reviewer.zot | |
| 80 | + | zut verify ./code-reviewer.zut | |
| 81 | + | zut run ./code-reviewer.zut | |
| 82 | 82 | ``` | |
| 83 | 83 | ||
| 84 | 84 | The first run displays the declared permissions and asks for consent before the agent starts. | |
| @@ -99,11 +99,11 @@ Only `manifest.json`. Other ordinary files and directories are included in the a | |||
| 99 | 99 | ||
| 100 | 100 | Do not include executable extensions in `extensions/`. The local runtime rejects any bundled extension directory containing `extension.json` because extension subprocesses cannot yet be confined to the manifest permissions. This is a deliberate fail-closed restriction. | |
| 101 | 101 | ||
| 102 | - | Symlinks are not supported and cause `zot pack` to fail. | |
| 102 | + | Symlinks are not supported and cause `zut pack` to fail. | |
| 103 | 103 | ||
| 104 | 104 | ## `AGENT.md` | |
| 105 | 105 | ||
| 106 | - | `AGENT.md` is optional. When present, it is appended to zot's system prompt by default. It should describe the agent's role, workflow, constraints, output format, and when to load bundled skills. When absent, it contributes no agent-specific text; other enabled global addenda still follow their normal rules. | |
| 106 | + | `AGENT.md` is optional. When present, it is appended to zut's system prompt by default. It should describe the agent's role, workflow, constraints, output format, and when to load bundled skills. When absent, it contributes no agent-specific text; other enabled global addenda still follow their normal rules. | |
| 107 | 107 | ||
| 108 | 108 | Keep capability and security declarations out of this file. Permissions come only from `manifest.json` and are enforced independently of agent-authored prose. | |
| 109 | 109 | ||
| @@ -115,11 +115,11 @@ Set `replace_system_prompt` to `true` in the manifest to use `AGENT.md` as the r | |||
| 115 | 115 | } | |
| 116 | 116 | ``` | |
| 117 | 117 | ||
| 118 | - | Replacement is intended for fully specialized agents. It replaces zot's built-in identity, while global append addenda such as enabled Ponytail coding guidance, project context, and skills retain their normal inclusion rules. The default layering behavior is usually preferable because it retains zot's normal identity and tool-use guidance. | |
| 118 | + | Replacement is intended for fully specialized agents. It replaces zut's built-in identity, while global append addenda such as enabled Ponytail coding guidance, project context, and skills retain their normal inclusion rules. The default layering behavior is usually preferable because it retains zut's normal identity and tool-use guidance. | |
| 119 | 119 | ||
| 120 | 120 | ## Bundled skills | |
| 121 | 121 | ||
| 122 | - | Place skills under `skills/<name>/SKILL.md` using the normal zot skill format: | |
| 122 | + | Place skills under `skills/<name>/SKILL.md` using the normal zut skill format: | |
| 123 | 123 | ||
| 124 | 124 | ```text | |
| 125 | 125 | skills/ | |
| @@ -141,7 +141,7 @@ description: Diagnose a failing command from its output and relevant source file | |||
| 141 | 141 | 4. Propose the smallest correction and validation plan. | |
| 142 | 142 | ``` | |
| 143 | 143 | ||
| 144 | - | Bundled skills are added to normal skill discovery while the zotfile is running. The model sees their name and description in the skill manifest and can load the full body through the `skill` tool. See [skills.md](skills.md) for the complete skill format. | |
| 144 | + | Bundled skills are added to normal skill discovery while the zutfile is running. The model sees their name and description in the skill manifest and can load the full body through the `skill` tool. See [skills.md](skills.md) for the complete skill format. | |
| 145 | 145 | ||
| 146 | 146 | ## Manifest reference | |
| 147 | 147 | ||
| @@ -149,13 +149,13 @@ The current manifest shape is: | |||
| 149 | 149 | ||
| 150 | 150 | ```json | |
| 151 | 151 | { | |
| 152 | - | "zotfile": 1, | |
| 152 | + | "zutfile": 1, | |
| 153 | 153 | "name": "code-reviewer", | |
| 154 | 154 | "version": "0.1.0", | |
| 155 | 155 | "description": "Reviews a repository and reports actionable findings.", | |
| 156 | 156 | "license": "MIT", | |
| 157 | 157 | "runtime": { | |
| 158 | - | "min_zot": "0.2.76" | |
| 158 | + | "min_zut": "0.2.76" | |
| 159 | 159 | }, | |
| 160 | 160 | "model": { | |
| 161 | 161 | "requires": ["tools", "reasoning"], | |
| @@ -195,12 +195,12 @@ The current manifest shape is: | |||
| 195 | 195 | ||
| 196 | 196 | | Field | Required | Current behavior | | |
| 197 | 197 | |---|---:|---| | |
| 198 | - | | `zotfile` | yes | Format version. Must be `1`. | | |
| 198 | + | | `zutfile` | yes | Format version. Must be `1`. | | |
| 199 | 199 | | `name` | yes | Local agent identity and session/data namespace. See naming rules below. | | |
| 200 | 200 | | `version` | no | Displayed in consent and inspection output. Semver is recommended but not currently validated. | | |
| 201 | - | | `description` | no | Displayed by `zot inspect`. | | |
| 201 | + | | `description` | no | Displayed by `zut inspect`. | | |
| 202 | 202 | | `license` | no | Package metadata. Not otherwise interpreted by the local runtime. | | |
| 203 | - | | `runtime.min_zot` | no | Minimum zot version. Older binaries refuse to run the agent. Unversioned development builds cannot satisfy a non-empty minimum. | | |
| 203 | + | | `runtime.min_zut` | no | Minimum zut version. Older binaries refuse to run the agent. Unversioned development builds cannot satisfy a non-empty minimum. | | |
| 204 | 204 | | `model` | no | Model capabilities, minimum context, and preferences. | | |
| 205 | 205 | | `permissions` | no | Filesystem and bash permission ceiling. Omitted scopes deny access. | | |
| 206 | 206 | | `requirements` | no | Required operating systems and executables. | | |
| @@ -244,7 +244,7 @@ Unknown capabilities are rejected. Use `tools`, not the draft spelling `tool_cal | |||
| 244 | 244 | 1. An explicitly selected model, if present, must satisfy the requirements. | |
| 245 | 245 | 2. The user's configured default is retained if it satisfies them. | |
| 246 | 246 | 3. The first compatible model in `preferred` is selected. | |
| 247 | - | 4. Otherwise, zot chooses a compatible active catalog model. | |
| 247 | + | 4. Otherwise, zut chooses a compatible active catalog model. | |
| 248 | 248 | 5. If none qualifies, execution stops with an error. | |
| 249 | 249 | ||
| 250 | 250 | `model.min_tier` is present in the format shape but is not supported by the local runtime. Leave it empty or omit it. A non-empty value is rejected. | |
| @@ -271,11 +271,11 @@ Use `requirements.bin` for commands that must already be on `PATH`: | |||
| 271 | 271 | } | |
| 272 | 272 | ``` | |
| 273 | 273 | ||
| 274 | - | Zot checks these requirements before requesting consent. These zotfiles do not have install or postinstall hooks, so authors must document how users can obtain missing programs. | |
| 274 | + | Zut checks these requirements before requesting consent. These zutfiles do not have install or postinstall hooks, so authors must document how users can obtain missing programs. | |
| 275 | 275 | ||
| 276 | 276 | ### Entry fields | |
| 277 | 277 | ||
| 278 | - | `entry.pre` is auto-submitted once when `zot run` starts, before the initial prompt is applied. Values that begin with `!` use the interactive shell-escape path (or `BashTool` in `--print` / `--stream` / `--json`). Shell output streams live to the TUI (interactive) or stderr (non-interactive). Other values are sent as a normal user turn; in `--stream` mode that turn's assistant text also streams to stdout. After `pre` finishes, zot reloads extensions and rediscovers skills so anything installed by that command is available for the following turn. | |
| 278 | + | `entry.pre` is auto-submitted once when `zut run` starts, before the initial prompt is applied. Values that begin with `!` use the interactive shell-escape path (or `BashTool` in `--print` / `--stream` / `--json`). Shell output streams live to the TUI (interactive) or stderr (non-interactive). Other values are sent as a normal user turn; in `--stream` mode that turn's assistant text also streams to stdout. After `pre` finishes, zut reloads extensions and rediscovers skills so anything installed by that command is available for the following turn. | |
| 279 | 279 | ||
| 280 | 280 | ```json | |
| 281 | 281 | { | |
| @@ -310,7 +310,7 @@ Two variables are available in filesystem scopes: | |||
| 310 | 310 | | Variable | Resolves to | | |
| 311 | 311 | |---|---| | |
| 312 | 312 | | `${workspace}` | The current working directory used for the run. | | |
| 313 | - | | `${agent_data}` | The agent's persistent private data directory under `$ZOT_HOME/agents/<name>/data/`. | | |
| 313 | + | | `${agent_data}` | The agent's persistent private data directory under `$ZUT_HOME/agents/<name>/data/`. | | |
| 314 | 314 | ||
| 315 | 315 | Relative paths are resolved beneath `${workspace}`. For example: | |
| 316 | 316 | ||
| @@ -412,7 +412,7 @@ In the current local runtime, `ask` is launch-time capability consent. It does n | |||
| 412 | 412 | } | |
| 413 | 413 | ``` | |
| 414 | 414 | ||
| 415 | - | Every command in a shell expression must be listed. Zot checks commands separated by `;`, `&&`, `||`, and pipes. Paths are reduced to their base command name, so `/usr/bin/git` is checked as `git`. A leading environment assignment is skipped when identifying the command. | |
| 415 | + | Every command in a shell expression must be listed. Zut checks commands separated by `;`, `&&`, `||`, and pipes. Paths are reduced to their base command name, so `/usr/bin/git` is checked as `git`. A leading environment assignment is skipped when identifying the command. | |
| 416 | 416 | ||
| 417 | 417 | The allowlist rejects shell substitution, newlines, redirection, backticks, `$()`, `<`, and `>`. This intentionally supports simple command pipelines rather than arbitrary shell programs. | |
| 418 | 418 | ||
| @@ -439,7 +439,7 @@ Bundled executable extensions are rejected for the same reason. Network filterin | |||
| 439 | 439 | ||
| 440 | 440 | ## Consent and persistent data | |
| 441 | 441 | ||
| 442 | - | Before the first run, zot prints the agent identity and expanded filesystem and bash permissions, then asks: | |
| 442 | + | Before the first run, zut prints the agent identity and expanded filesystem and bash permissions, then asks: | |
| 443 | 443 | ||
| 444 | 444 | ```text | |
| 445 | 445 | Agent code-reviewer@0.1.0 wants to run. | |
| @@ -454,7 +454,7 @@ Allow? [y/N] | |||
| 454 | 454 | For modes other than `bash: ask`, approval is cached for the exact artifact digest under: | |
| 455 | 455 | ||
| 456 | 456 | ```text | |
| 457 | - | $ZOT_HOME/agents/<name>/consents/<digest>.json | |
| 457 | + | $ZUT_HOME/agents/<name>/consents/<digest>.json | |
| 458 | 458 | ``` | |
| 459 | 459 | ||
| 460 | 460 | Any change to the packaged artifact produces a different digest and requires consent again. `bash: ask` always requires fresh launch consent. | |
| @@ -462,11 +462,11 @@ Any change to the packaged artifact produces a different digest and requires con | |||
| 462 | 462 | Non-interactive runs refuse to bypass consent by default. For controlled automation, pass `-y` / `--yes` or set: | |
| 463 | 463 | ||
| 464 | 464 | ```bash | |
| 465 | - | zot run -y ./code-reviewer.zot --print "Review this repository" | |
| 465 | + | zut run -y ./code-reviewer.zut --print "Review this repository" | |
| 466 | 466 | ``` | |
| 467 | 467 | ||
| 468 | 468 | ```bash | |
| 469 | - | ZOT_AGENT_CONSENT=1 zot run ./code-reviewer.zot --print "Review this repository" | |
| 469 | + | ZUT_AGENT_CONSENT=1 zut run ./code-reviewer.zut --print "Review this repository" | |
| 470 | 470 | ``` | |
| 471 | 471 | ||
| 472 | 472 | This environment variable skips the consent prompt. Only use it after independently inspecting and trusting the exact artifact being run. | |
| @@ -474,116 +474,116 @@ This environment variable skips the consent prompt. Only use it after independen | |||
| 474 | 474 | Persistent agent data lives under: | |
| 475 | 475 | ||
| 476 | 476 | ```text | |
| 477 | - | $ZOT_HOME/agents/<name>/data/ | |
| 477 | + | $ZUT_HOME/agents/<name>/data/ | |
| 478 | 478 | ``` | |
| 479 | 479 | ||
| 480 | 480 | The directory is created for every run, but the agent can access it only when `${agent_data}` appears in the relevant filesystem permission scope. | |
| 481 | 481 | ||
| 482 | 482 | ## Agent-scoped sessions | |
| 483 | 483 | ||
| 484 | - | Sessions created by a zotfile are isolated from ordinary zot sessions and from other agents: | |
| 484 | + | Sessions created by a zutfile are isolated from ordinary zut sessions and from other agents: | |
| 485 | 485 | ||
| 486 | 486 | ```text | |
| 487 | - | $ZOT_HOME/sessions/agents/<name>/ | |
| 487 | + | $ZUT_HOME/sessions/agents/<name>/ | |
| 488 | 488 | ``` | |
| 489 | 489 | ||
| 490 | 490 | Normal session flags still apply, including `--continue`, `--resume`, `--session`, and `--no-session`, but their default storage root is scoped to the active agent. The interactive `/sessions` picker and `/session` import, fork, and tree commands use that same agent-scoped root; `/fork` is the direct alias for selecting a user message and branching after that turn. `/session tree` shows the current session's family within that root (its ancestor and descendants), keeps pre-compaction user and assistant history available for checkout, and hides tool/internal rows from the fork picker. Tree-navigation branches stay hidden from the flat `/sessions` picker. Resuming honors the selected session's stored provider/model pair; a failed provider/model rebuild leaves the current session unchanged. | |
| 491 | 491 | ||
| 492 | 492 | ## Commands | |
| 493 | 493 | ||
| 494 | - | ### `zot run` | |
| 494 | + | ### `zut run` | |
| 495 | 495 | ||
| 496 | 496 | Run an unpackaged directory during development: | |
| 497 | 497 | ||
| 498 | 498 | ```bash | |
| 499 | - | zot run ./my-agent | |
| 500 | - | zot run -y ./my-agent | |
| 501 | - | zot run ./my-agent "Do the task" | |
| 502 | - | zot run ./my-agent -y --print "Do the task" | |
| 499 | + | zut run ./my-agent | |
| 500 | + | zut run -y ./my-agent | |
| 501 | + | zut run ./my-agent "Do the task" | |
| 502 | + | zut run ./my-agent -y --print "Do the task" | |
| 503 | 503 | ``` | |
| 504 | 504 | ||
| 505 | 505 | Run a packed artifact: | |
| 506 | 506 | ||
| 507 | 507 | ```bash | |
| 508 | - | zot run ./my-agent.zot | |
| 509 | - | zot run ./my-agent.zot --print "Do the task" | |
| 510 | - | zot run ./my-agent.zot --stream "Do the task" | |
| 511 | - | zot run ./my-agent.zot --json "Do the task" | |
| 508 | + | zut run ./my-agent.zut | |
| 509 | + | zut run ./my-agent.zut --print "Do the task" | |
| 510 | + | zut run ./my-agent.zut --stream "Do the task" | |
| 511 | + | zut run ./my-agent.zut --json "Do the task" | |
| 512 | 512 | ``` | |
| 513 | 513 | ||
| 514 | - | Arguments after the reference use zot's normal CLI parser, so model, provider, reasoning, cwd, session, tool, and output-mode flags remain available. The manifest still imposes its permission and compatibility ceiling. In interactive mode, enabling **show loaded resources at startup** in `/settings` displays the manifest name in an `[Agent]` section above the transcript. | |
| 514 | + | Arguments after the reference use zut's normal CLI parser, so model, provider, reasoning, cwd, session, tool, and output-mode flags remain available. The manifest still imposes its permission and compatibility ceiling. In interactive mode, enabling **show loaded resources at startup** in `/settings` displays the manifest name in an `[Agent]` section above the transcript. | |
| 515 | 515 | ||
| 516 | - | Local filesystem directories, local archive paths, GitHub shorthand, and public GitHub agent-directory URLs are accepted. A single-part name resolves only to a matching local directory or `.zot` archive: | |
| 516 | + | Local filesystem directories, local archive paths, GitHub shorthand, and public GitHub agent-directory URLs are accepted. A single-part name resolves only to a matching local directory or `.zut` archive: | |
| 517 | 517 | ||
| 518 | 518 | ```bash | |
| 519 | - | zot run my-agent | |
| 519 | + | zut run my-agent | |
| 520 | 520 | ``` | |
| 521 | 521 | ||
| 522 | 522 | A two-part name is treated as a GitHub `owner/repository` reference. Resolution remains local-first, then uses the repository root: | |
| 523 | 523 | ||
| 524 | 524 | ```bash | |
| 525 | - | zot run frkr/zot-archify | |
| 525 | + | zut run frkr/zot-archify | |
| 526 | 526 | ``` | |
| 527 | 527 | ||
| 528 | 528 | Use three or more parts to select an agent directory from any public GitHub collection: | |
| 529 | 529 | ||
| 530 | 530 | ```bash | |
| 531 | - | zot run acme/agents/reviewer | |
| 532 | - | zot run acme/clankers/reviewers/go | |
| 531 | + | zut run acme/agents/reviewer | |
| 532 | + | zut run acme/clankers/reviewers/go | |
| 533 | 533 | ``` | |
| 534 | 534 | ||
| 535 | - | There is no built-in owner, official collection, collection configuration, or allowlist. Every shorthand part must use lowercase letters, digits, dots, hyphens, or underscores. Backslashes are also accepted as separators on Windows. Prefix a reference with `./`, use an absolute path, retain the `.zot` suffix, or provide a complete GitHub URL to force local or explicit remote resolution. | |
| 535 | + | There is no built-in owner, official collection, collection configuration, or allowlist. Every shorthand part must use lowercase letters, digits, dots, hyphens, or underscores. Backslashes are also accepted as separators on Windows. Prefix a reference with `./`, use an absolute path, retain the `.zut` suffix, or provide a complete GitHub URL to force local or explicit remote resolution. | |
| 536 | 536 | ||
| 537 | - | For GitHub, zot downloads the repository archive into a temporary directory, selects the requested agent subdirectory, validates it, runs it, and removes the downloaded files when the command exits: | |
| 537 | + | For GitHub, zut downloads the repository archive into a temporary directory, selects the requested agent subdirectory, validates it, runs it, and removes the downloaded files when the command exits: | |
| 538 | 538 | ||
| 539 | 539 | ```bash | |
| 540 | - | zot run https://github.com/acme/agents/reviewer | |
| 540 | + | zut run https://github.com/acme/agents/reviewer | |
| 541 | 541 | ``` | |
| 542 | 542 | ||
| 543 | 543 | A standard GitHub tree URL is also supported: | |
| 544 | 544 | ||
| 545 | 545 | ```bash | |
| 546 | - | zot run https://github.com/acme/agents/tree/main/reviewer | |
| 546 | + | zut run https://github.com/acme/agents/tree/main/reviewer | |
| 547 | 547 | ``` | |
| 548 | 548 | ||
| 549 | - | Short forms read the repository's default branch through GitHub's `HEAD` archive. A tree URL uses the branch or tag in the URL. Private repositories and branch or tag names containing `/` are not currently supported. The downloaded source is temporary, but normal agent data, consent receipts, and session transcripts remain under `$ZOT_HOME`. | |
| 549 | + | Short forms read the repository's default branch through GitHub's `HEAD` archive. A tree URL uses the branch or tag in the URL. Private repositories and branch or tag names containing `/` are not currently supported. The downloaded source is temporary, but normal agent data, consent receipts, and session transcripts remain under `$ZUT_HOME`. | |
| 550 | 550 | ||
| 551 | 551 | `owner/repository` and `owner/repository/path` are direct GitHub mappings, not an indexed or signed registry. Installed names, arbitrary URLs, and OCI references are not resolved yet. | |
| 552 | 552 | ||
| 553 | - | ### `zot pack` | |
| 553 | + | ### `zut pack` | |
| 554 | 554 | ||
| 555 | 555 | ```bash | |
| 556 | - | zot pack [directory] [output] | |
| 556 | + | zut pack [directory] [output] | |
| 557 | 557 | ``` | |
| 558 | 558 | ||
| 559 | 559 | Examples: | |
| 560 | 560 | ||
| 561 | 561 | ```bash | |
| 562 | - | zot pack | |
| 563 | - | zot pack ./my-agent | |
| 564 | - | zot pack ./my-agent ./dist/reviewer.zot | |
| 562 | + | zut pack | |
| 563 | + | zut pack ./my-agent | |
| 564 | + | zut pack ./my-agent ./dist/reviewer.zut | |
| 565 | 565 | ``` | |
| 566 | 566 | ||
| 567 | - | The default directory is the current directory. When output is omitted, zot uses `<manifest-name>.zot`. If the output has no `.zot` suffix, zot adds one. | |
| 567 | + | The default directory is the current directory. When output is omitted, zut uses `<manifest-name>.zut`. If the output has no `.zut` suffix, zut adds one. | |
| 568 | 568 | ||
| 569 | - | Packing validates the manifest and directory, then creates a zstd-compressed tar archive. Entries are sorted and tar metadata is normalized with fixed timestamps and numeric ownership. The output archive itself is excluded when it is located inside the source directory. Zot prints the SHA-256 digest of the resulting compressed artifact. | |
| 569 | + | Packing validates the manifest and directory, then creates a zstd-compressed tar archive. Entries are sorted and tar metadata is normalized with fixed timestamps and numeric ownership. The output archive itself is excluded when it is located inside the source directory. Zut prints the SHA-256 digest of the resulting compressed artifact. | |
| 570 | 570 | ||
| 571 | - | ### `zot inspect` | |
| 571 | + | ### `zut inspect` | |
| 572 | 572 | ||
| 573 | 573 | ```bash | |
| 574 | - | zot inspect ./my-agent | |
| 575 | - | zot inspect ./my-agent.zot | |
| 576 | - | zot inspect https://github.com/acme/agents/code-reviewer | |
| 574 | + | zut inspect ./my-agent | |
| 575 | + | zut inspect ./my-agent.zut | |
| 576 | + | zut inspect https://github.com/acme/agents/code-reviewer | |
| 577 | 577 | ``` | |
| 578 | 578 | ||
| 579 | 579 | Inspection validates and prints the agent's name, version, description, digest, declared permissions, and complete file list. It does not execute the agent. | |
| 580 | 580 | ||
| 581 | - | For a directory, the digest is computed from its canonical uncompressed tar representation. For a `.zot` archive, the digest is computed from the archive bytes. Therefore a source-directory digest and its packed-archive digest are not expected to match. | |
| 581 | + | For a directory, the digest is computed from its canonical uncompressed tar representation. For a `.zut` archive, the digest is computed from the archive bytes. Therefore a source-directory digest and its packed-archive digest are not expected to match. | |
| 582 | 582 | ||
| 583 | - | ### `zot verify` | |
| 583 | + | ### `zut verify` | |
| 584 | 584 | ||
| 585 | 585 | ```bash | |
| 586 | - | zot verify ./my-agent.zot | |
| 586 | + | zut verify ./my-agent.zut | |
| 587 | 587 | ``` | |
| 588 | 588 | ||
| 589 | 589 | The local `verify` command validates that the archive can be safely loaded, validates the manifest and required layout, and prints its SHA-256 digest. | |
| @@ -596,7 +596,7 @@ The archive loader applies these limits: | |||
| 596 | 596 | ||
| 597 | 597 | | Limit | Value | | |
| 598 | 598 | |---|---:| | |
| 599 | - | | Compressed `.zot` file | 100 MiB | | |
| 599 | + | | Compressed `.zut` file | 100 MiB | | |
| 600 | 600 | | One extracted entry | 64 MiB | | |
| 601 | 601 | | Total extracted content | 256 MiB | | |
| 602 | 602 | ||
| @@ -607,7 +607,7 @@ Archive extraction rejects absolute paths and parent traversal. Unsupported tar | |||
| 607 | 607 | 1. Start with the narrowest permissions possible. A reviewer usually needs repository reads, not writes or bash. | |
| 608 | 608 | 2. Keep `AGENT.md` focused on stable behavior. Put task-specific procedures in skills so the model loads them only when relevant. | |
| 609 | 609 | 3. Declare every external command in both `requirements.bin` and the bash allowlist when the agent must execute it. | |
| 610 | - | 4. Test from a directory first with `zot inspect` and `zot run`. | |
| 610 | + | 4. Test from a directory first with `zut inspect` and `zut run`. | |
| 611 | 611 | 5. Test denied behavior as well as allowed behavior. Ask the agent to attempt an out-of-scope read, write, and command. | |
| 612 | 612 | 6. Pack the same source twice and compare behavior and output digests before distributing it. | |
| 613 | 613 | 7. Inspect and verify the final archive, not only its source directory. | |
| @@ -617,11 +617,11 @@ Archive extraction rejects absolute paths and parent traversal. Unsupported tar | |||
| 617 | 617 | ||
| 618 | 618 | Implemented now: | |
| 619 | 619 | ||
| 620 | - | - local directories and `.zot` archives | |
| 620 | + | - local directories and `.zut` archives | |
| 621 | 621 | - local-only resolution for single-part names | |
| 622 | 622 | - local-first `owner/repository` and `owner/repository/path` resolution to any public GitHub repository | |
| 623 | 623 | - temporary execution of agent directories from public GitHub repositories | |
| 624 | - | - `zot pack`, `zot inspect`, `zot verify`, and `zot run` | |
| 624 | + | - `zut pack`, `zut inspect`, `zut verify`, and `zut run` | |
| 625 | 625 | - canonical tar creation with zstd compression | |
| 626 | 626 | - archive digest reporting and safe extraction limits | |
| 627 | 627 | - `AGENT.md` append or replacement behavior | |
| @@ -636,8 +636,8 @@ Implemented now: | |||
| 636 | 636 | ||
| 637 | 637 | Not implemented yet: | |
| 638 | 638 | ||
| 639 | - | - `zot install`, `zot agents`, `zot use`, `zot update`, or `zot publish` | |
| 640 | - | - installed-name, arbitrary URL, OCI, configurable registry, or zot.sh index resolution | |
| 639 | + | - `zut install`, `zut agents`, `zut use`, `zut update`, or `zut publish` | |
| 640 | + | - installed-name, arbitrary URL, OCI, configurable registry, or GitHub index resolution | |
| 641 | 641 | - network allowlist enforcement | |
| 642 | 642 | - environment-variable filtering | |
| 643 | 643 | - safely confined bundled executable extensions | |
| @@ -646,4 +646,4 @@ Not implemented yet: | |||
| 646 | 646 | - per-agent Telegram, bot, or RPC selection | |
| 647 | 647 | - `--no-sandbox`, `--trust-bash`, or registry trust override flags | |
| 648 | 648 | ||
| 649 | - | Treat the unsupported fields and commands in the broader zotfile proposal as forward-looking design, not as current runtime behavior. | |
| 649 | + | Treat the unsupported fields and commands in the broader zutfile proposal as forward-looking design, not as current runtime behavior. | |
M examples/extensions/approve/README.md +3 -3
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # approve — example zot extension | |
| 1 | + | # approve — example zut extension | |
| 2 | 2 | ||
| 3 | 3 | Demonstrates the **spontaneous `open_panel`** pattern: an | |
| 4 | 4 | extension-registered tool opens a panel from inside its handler | |
| @@ -39,12 +39,12 @@ go build -o approve . | |||
| 39 | 39 | ## Install | |
| 40 | 40 | ||
| 41 | 41 | ```bash | |
| 42 | - | zot ext install . | |
| 42 | + | zut ext install . | |
| 43 | 43 | ``` | |
| 44 | 44 | ||
| 45 | 45 | ## Try it | |
| 46 | 46 | ||
| 47 | - | In zot, ask: | |
| 47 | + | In zut, ask: | |
| 48 | 48 | ||
| 49 | 49 | > Request approval to delete the temp directory. | |
| 50 | 50 | ||
M examples/extensions/approve/main.go +3 -3
| @@ -16,9 +16,9 @@ | |||
| 16 | 16 | // | |
| 17 | 17 | // Install: | |
| 18 | 18 | // | |
| 19 | - | // zot ext install . | |
| 19 | + | // zut ext install . | |
| 20 | 20 | // | |
| 21 | - | // Try it — ask zot something like: | |
| 21 | + | // Try it — ask zut something like: | |
| 22 | 22 | // | |
| 23 | 23 | // "Request approval to delete the temp directory." | |
| 24 | 24 | // | |
| @@ -35,7 +35,7 @@ import ( | |||
| 35 | 35 | "strings" | |
| 36 | 36 | "sync" | |
| 37 | 37 | ||
| 38 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 38 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 39 | 39 | ) | |
| 40 | 40 | ||
| 41 | 41 | const schema = `{ | |
M examples/extensions/clock/README.md +6 -6
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # clock — example zot extension (Node, no dependencies) | |
| 1 | + | # clock — example zut extension (Node, no dependencies) | |
| 2 | 2 | ||
| 3 | 3 | A minimal TypeScript-style extension showing the wire protocol from | |
| 4 | 4 | the Node side without any SDK. Pure stdlib (`readline`, `process`). | |
| @@ -12,15 +12,15 @@ Node 18 or newer (uses ESM). No `npm install` step. | |||
| 12 | 12 | From this directory: | |
| 13 | 13 | ||
| 14 | 14 | ```bash | |
| 15 | - | zot ext install . | |
| 15 | + | zut ext install . | |
| 16 | 16 | ``` | |
| 17 | 17 | ||
| 18 | - | This copies the manifest + script into `$ZOT_HOME/extensions/clock/`. | |
| 19 | - | zot picks it up the next time you launch the TUI. | |
| 18 | + | This copies the manifest + script into `$ZUT_HOME/extensions/clock/`. | |
| 19 | + | zut picks it up the next time you launch the TUI. | |
| 20 | 20 | ||
| 21 | 21 | ## Use | |
| 22 | 22 | ||
| 23 | - | In zot: | |
| 23 | + | In zut: | |
| 24 | 24 | ||
| 25 | 25 | - `/now` — extension pushes a styled note showing local | |
| 26 | 26 | and ISO time (no model call) | |
| @@ -44,7 +44,7 @@ authoring works too — rename `index.js` → `index.ts`, install | |||
| 44 | 44 | } | |
| 45 | 45 | ``` | |
| 46 | 46 | ||
| 47 | - | zot doesn't care which one you use; it just spawns whatever `exec` | |
| 47 | + | zut doesn't care which one you use; it just spawns whatever `exec` | |
| 48 | 48 | points at and reads/writes JSON lines on its stdio. | |
| 49 | 49 | ||
| 50 | 50 | ## See also | |
M examples/extensions/clock/index.js +15 -8
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // clock — a zot extension written in plain Node (no dependencies). | |
| 1 | + | // clock — a zut extension written in plain Node (no dependencies). | |
| 2 | 2 | // | |
| 3 | 3 | // Registers two slash commands: | |
| 4 | 4 | // /now — pushes the current local time into the chat as | |
| @@ -15,9 +15,9 @@ | |||
| 15 | 15 | // stdout. | |
| 16 | 16 | // | |
| 17 | 17 | // Install: | |
| 18 | - | // zot ext install /path/to/this/dir | |
| 18 | + | // zut ext install /path/to/this/dir | |
| 19 | 19 | // | |
| 20 | - | // Then in zot: | |
| 20 | + | // Then in zut: | |
| 21 | 21 | // /now | |
| 22 | 22 | // /uptime | |
| 23 | 23 | ||
| @@ -31,7 +31,7 @@ const STARTED_AT = Date.now(); | |||
| 31 | 31 | /** @typedef {{type: string, id?: string, [k: string]: unknown}} Frame */ | |
| 32 | 32 | ||
| 33 | 33 | /** | |
| 34 | - | * Send a frame to zot. One JSON object per line; flush immediately | |
| 34 | + | * Send a frame to zut. One JSON object per line; flush immediately | |
| 35 | 35 | * so the host doesn't sit waiting on a buffer. | |
| 36 | 36 | * @param {Frame} obj | |
| 37 | 37 | */ | |
| @@ -40,7 +40,7 @@ function send(obj) { | |||
| 40 | 40 | } | |
| 41 | 41 | ||
| 42 | 42 | /** | |
| 43 | - | * stderr is captured by zot to $ZOT_HOME/logs/ext-clock.log; perfect | |
| 43 | + | * stderr is captured by zut to $ZUT_HOME/logs/ext-clock.log; perfect | |
| 44 | 44 | * for debug output. Anything written to stdout would corrupt the | |
| 45 | 45 | * protocol stream. | |
| 46 | 46 | * @param {string} msg | |
| @@ -52,6 +52,7 @@ function log(msg) { | |||
| 52 | 52 | // 1. Hello first. | |
| 53 | 53 | send({ | |
| 54 | 54 | type: "hello", | |
| 55 | + | protocol_version: 2, | |
| 55 | 56 | name: NAME, | |
| 56 | 57 | version: VERSION, | |
| 57 | 58 | capabilities: ["commands"], | |
| @@ -69,7 +70,7 @@ send({ | |||
| 69 | 70 | description: "ask the agent to riff on how long the clock ext has run", | |
| 70 | 71 | }); | |
| 71 | 72 | ||
| 72 | - | // 3. Read frames until stdin closes (zot shuts us down). | |
| 73 | + | // 3. Read frames until stdin closes (zut shuts us down). | |
| 73 | 74 | const rl = createInterface({ input: stdin, crlfDelay: Infinity }); | |
| 74 | 75 | ||
| 75 | 76 | rl.on("line", (line) => { | |
| @@ -84,8 +85,14 @@ rl.on("line", (line) => { | |||
| 84 | 85 | ||
| 85 | 86 | switch (frame.type) { | |
| 86 | 87 | case "hello_ack": | |
| 88 | + | if (frame.protocol_version !== 2) { | |
| 89 | + | log(`unsupported protocol version: ${frame.protocol_version}`); | |
| 90 | + | process.exitCode = 1; | |
| 91 | + | rl.close(); | |
| 92 | + | break; | |
| 93 | + | } | |
| 87 | 94 | log( | |
| 88 | - | `connected to zot ${frame.zot_version} (${frame.provider}/${frame.model})`, | |
| 95 | + | `connected to zut ${frame.zut_version} (${frame.provider}/${frame.model})`, | |
| 89 | 96 | ); | |
| 90 | 97 | break; | |
| 91 | 98 | ||
| @@ -147,7 +154,7 @@ function handleCommand(frame) { | |||
| 147 | 154 | id, | |
| 148 | 155 | action: "prompt", | |
| 149 | 156 | prompt: | |
| 150 | - | `The clock extension has been running for ${seconds}s in this zot session. ` + | |
| 157 | + | `The clock extension has been running for ${seconds}s in this zut session. ` + | |
| 151 | 158 | `Riff on that briefly in one short sentence — be a little dramatic. ${focus}`.trim(), | |
| 152 | 159 | }); | |
| 153 | 160 | return; | |
M examples/extensions/guard/README.md +5 -5
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # guard — example zot extension (Go, phase 3) | |
| 1 | + | # guard — example zut extension (Go, phase 3) | |
| 2 | 2 | ||
| 3 | 3 | Demonstrates the **event subscription** and **tool-call interception** | |
| 4 | 4 | half of the extension protocol (phase 3). | |
| @@ -6,7 +6,7 @@ half of the extension protocol (phase 3). | |||
| 6 | 6 | What it does: | |
| 7 | 7 | ||
| 8 | 8 | - Subscribes to `session_start`, `turn_start`, `tool_call`, `turn_end` | |
| 9 | - | and appends a line to `/tmp/zot-guard-audit.log` for each. | |
| 9 | + | and appends a line to `/tmp/zut-guard-audit.log` for each. | |
| 10 | 10 | - Intercepts every `bash` tool call. If the command matches a danger | |
| 11 | 11 | regex (`rm -rf`, `sudo`, `dd of=/`, `mkfs`, the fork bomb, `chmod -R | |
| 12 | 12 | 777`), the call is **refused**. The model sees the refusal as the | |
| @@ -23,12 +23,12 @@ go build -o guard . | |||
| 23 | 23 | ## Install | |
| 24 | 24 | ||
| 25 | 25 | ```bash | |
| 26 | - | zot ext install . | |
| 26 | + | zut ext install . | |
| 27 | 27 | ``` | |
| 28 | 28 | ||
| 29 | 29 | ## Try it | |
| 30 | 30 | ||
| 31 | - | In zot, ask: | |
| 31 | + | In zut, ask: | |
| 32 | 32 | ||
| 33 | 33 | > Run `rm -rf /tmp/foo` | |
| 34 | 34 | ||
| @@ -42,7 +42,7 @@ Allowed; the audit log records the call. | |||
| 42 | 42 | Tail the audit log: | |
| 43 | 43 | ||
| 44 | 44 | ```bash | |
| 45 | - | tail -f /tmp/zot-guard-audit.log | |
| 45 | + | tail -f /tmp/zut-guard-audit.log | |
| 46 | 46 | ``` | |
| 47 | 47 | ||
| 48 | 48 | ## Extending the danger list | |
M examples/extensions/guard/main.go +4 -4
| @@ -15,8 +15,8 @@ | |||
| 15 | 15 | // cd examples/extensions/guard | |
| 16 | 16 | // go build -o guard . | |
| 17 | 17 | // | |
| 18 | - | // Drop it next to its extension.json under $ZOT_HOME/extensions/guard/ | |
| 19 | - | // (or `zot ext install ./guard` from this directory) and the next zot | |
| 18 | + | // Drop it next to its extension.json under $ZUT_HOME/extensions/guard/ | |
| 19 | + | // (or `zut ext install ./guard` from this directory) and the next zut | |
| 20 | 20 | // session will load it automatically. | |
| 21 | 21 | package main | |
| 22 | 22 | ||
| @@ -29,7 +29,7 @@ import ( | |||
| 29 | 29 | "strings" | |
| 30 | 30 | "time" | |
| 31 | 31 | ||
| 32 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 32 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 33 | 33 | ) | |
| 34 | 34 | ||
| 35 | 35 | var dangerPatterns = []*regexp.Regexp{ | |
| @@ -44,7 +44,7 @@ var dangerPatterns = []*regexp.Regexp{ | |||
| 44 | 44 | func main() { | |
| 45 | 45 | e := ext.New("guard", "1.0.0") | |
| 46 | 46 | ||
| 47 | - | auditPath := filepath.Join(os.TempDir(), "zot-guard-audit.log") | |
| 47 | + | auditPath := filepath.Join(os.TempDir(), "zut-guard-audit.log") | |
| 48 | 48 | auditFile, _ := os.OpenFile(auditPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644) | |
| 49 | 49 | if auditFile != nil { | |
| 50 | 50 | defer auditFile.Close() | |
M examples/extensions/hello/README.md +4 -4
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # hello — example zot extension (Go) | |
| 1 | + | # hello — example zut extension (Go) | |
| 2 | 2 | ||
| 3 | 3 | A minimal example showing how to register slash commands with the | |
| 4 | 4 | `ext` SDK. | |
| @@ -14,16 +14,16 @@ go build -o hello . | |||
| 14 | 14 | ||
| 15 | 15 | ```bash | |
| 16 | 16 | # from this directory | |
| 17 | - | zot ext install . | |
| 17 | + | zut ext install . | |
| 18 | 18 | ``` | |
| 19 | 19 | ||
| 20 | 20 | This copies the directory (manifest + binary) into | |
| 21 | - | `$ZOT_HOME/extensions/hello/`. zot picks it up the next time you | |
| 21 | + | `$ZUT_HOME/extensions/hello/`. zut picks it up the next time you | |
| 22 | 22 | launch the TUI. | |
| 23 | 23 | ||
| 24 | 24 | ## Use | |
| 25 | 25 | ||
| 26 | - | In zot, type: | |
| 26 | + | In zut, type: | |
| 27 | 27 | ||
| 28 | 28 | - `/hello` — agent greets you | |
| 29 | 29 | - `/hello <name>` — agent greets `<name>` | |
M examples/extensions/hello/main.go +3 -3
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // hello — a tiny zot extension that registers /hello and /summon. | |
| 1 | + | // hello — a tiny zut extension that registers /hello and /summon. | |
| 2 | 2 | // | |
| 3 | 3 | // Build it: | |
| 4 | 4 | // | |
| @@ -6,14 +6,14 @@ | |||
| 6 | 6 | // go build -o hello . | |
| 7 | 7 | // | |
| 8 | 8 | // Then drop it next to its extension.json under | |
| 9 | - | // $ZOT_HOME/extensions/hello/, or run `zot ext install ./hello` | |
| 9 | + | // $ZUT_HOME/extensions/hello/, or run `zut ext install ./hello` | |
| 10 | 10 | // from this directory. | |
| 11 | 11 | package main | |
| 12 | 12 | ||
| 13 | 13 | import ( | |
| 14 | 14 | "strings" | |
| 15 | 15 | ||
| 16 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 16 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | 19 | func main() { | |
M examples/extensions/mcp-bridge/Makefile +10 -10
| @@ -3,13 +3,13 @@ | |||
| 3 | 3 | BIN := $(notdir $(CURDIR)) | |
| 4 | 4 | # Must match "name" in extension.json (slash command/logical extension name). | |
| 5 | 5 | EXT := mcp | |
| 6 | - | ZOT_HOME ?= $(shell \ | |
| 6 | + | ZUT_HOME ?= $(shell \ | |
| 7 | 7 | if [ -n "$$XDG_STATE_HOME" ]; then \ | |
| 8 | - | printf '%s/zot' "$$XDG_STATE_HOME"; \ | |
| 8 | + | printf '%s/zut' "$$XDG_STATE_HOME"; \ | |
| 9 | 9 | elif [ "$$(uname -s)" = "Darwin" ]; then \ | |
| 10 | - | printf '%s/Library/Application Support/zot' "$$HOME"; \ | |
| 10 | + | printf '%s/Library/Application Support/zut' "$$HOME"; \ | |
| 11 | 11 | else \ | |
| 12 | - | printf '%s/.local/state/zot' "$$HOME"; \ | |
| 12 | + | printf '%s/.local/state/zut' "$$HOME"; \ | |
| 13 | 13 | fi) | |
| 14 | 14 | ||
| 15 | 15 | default: install | |
| @@ -18,11 +18,11 @@ build: | |||
| 18 | 18 | go build -o $(BIN) . | |
| 19 | 19 | ||
| 20 | 20 | install: | |
| 21 | - | -zot ext remove $(EXT) --yes | |
| 22 | - | rm -rf "$(ZOT_HOME)/extensions/$(BIN)" | |
| 23 | - | mkdir -p "$(ZOT_HOME)/extensions/$(EXT)" | |
| 24 | - | cp extension.json "$(ZOT_HOME)/extensions/$(EXT)/extension.json" | |
| 25 | - | go build -o "$(ZOT_HOME)/extensions/$(EXT)/$(BIN)" . | |
| 21 | + | -zut ext remove $(EXT) --yes | |
| 22 | + | rm -rf "$(ZUT_HOME)/extensions/$(BIN)" | |
| 23 | + | mkdir -p "$(ZUT_HOME)/extensions/$(EXT)" | |
| 24 | + | cp extension.json "$(ZUT_HOME)/extensions/$(EXT)/extension.json" | |
| 25 | + | go build -o "$(ZUT_HOME)/extensions/$(EXT)/$(BIN)" . | |
| 26 | 26 | ||
| 27 | 27 | uninstall: | |
| 28 | - | -zot ext remove $(EXT) --yes | |
| 28 | + | -zut ext remove $(EXT) --yes | |
M examples/extensions/mcp-bridge/README.md +17 -17
| @@ -1,8 +1,8 @@ | |||
| 1 | 1 | # mcp-bridge | |
| 2 | 2 | ||
| 3 | - | Connect zot to [MCP (Model Context Protocol)](https://modelcontextprotocol.io) servers. | |
| 3 | + | Connect zut to [MCP (Model Context Protocol)](https://modelcontextprotocol.io) servers. | |
| 4 | 4 | ||
| 5 | - | This extension reads MCP server configurations from standard locations (same format as Claude Desktop, Cursor, Cline, etc.) and bridges their tools into zot so the LLM can call them directly. | |
| 5 | + | This extension reads MCP server configurations from standard locations (same format as Claude Desktop, Cursor, Cline, etc.) and bridges their tools into zut so the LLM can call them directly. | |
| 6 | 6 | ||
| 7 | 7 | ## Features | |
| 8 | 8 | ||
| @@ -31,8 +31,8 @@ This extension reads MCP server configurations from standard locations (same for | |||
| 31 | 31 | 2. **Create a project config file:** | |
| 32 | 32 | ||
| 33 | 33 | ```bash | |
| 34 | - | mkdir -p .zot | |
| 35 | - | cat > .zot/mcp.json << 'EOF' | |
| 34 | + | mkdir -p .zut | |
| 35 | + | cat > .zut/mcp.json << 'EOF' | |
| 36 | 36 | { | |
| 37 | 37 | "mcpServers": { | |
| 38 | 38 | "filesystem": { | |
| @@ -51,10 +51,10 @@ This extension reads MCP server configurations from standard locations (same for | |||
| 51 | 51 | 3. **Install the extension:** | |
| 52 | 52 | ||
| 53 | 53 | ```bash | |
| 54 | - | zot ext install . | |
| 54 | + | zut ext install . | |
| 55 | 55 | ``` | |
| 56 | 56 | ||
| 57 | - | 4. **Restart zot.** On first run the extension refreshes its tool cache in the background. When zot shows `MCP tool cache changed`, run `/reload-ext` once. Future launches register the cached MCP tools immediately as deferred definitions. | |
| 57 | + | 4. **Restart zut.** On first run the extension refreshes its tool cache in the background. When zut shows `MCP tool cache changed`, run `/reload-ext` once. Future launches register the cached MCP tools immediately as deferred definitions. | |
| 58 | 58 | ||
| 59 | 59 | The model initially sees one small loader tool, `mcp__search_tools`. It searches cached MCP tool names and descriptions locally, activates up to eight relevant definitions by default, and then calls the selected MCP tool normally. This keeps large MCP installations compatible with providers that limit request or tool-schema size. | |
| 60 | 60 | ||
| @@ -64,12 +64,12 @@ Config files are loaded from two locations (project overrides global per-server) | |||
| 64 | 64 | ||
| 65 | 65 | | Location | Scope | | |
| 66 | 66 | |---|---| | |
| 67 | - | | `$ZOT_HOME/mcp.json` | Global (`$XDG_STATE_HOME/zot/mcp.json` when `XDG_STATE_HOME` is set) | | |
| 68 | - | | `.zot/mcp.json` | Project-level (in your project root) | | |
| 67 | + | | `$ZUT_HOME/mcp.json` | Global (`$XDG_STATE_HOME/zut/mcp.json` when `XDG_STATE_HOME` is set) | | |
| 68 | + | | `.zut/mcp.json` | Project-level (in your project root) | | |
| 69 | 69 | ||
| 70 | 70 | ### Config Format | |
| 71 | 71 | ||
| 72 | - | Standard MCP config — same as Claude Desktop, with zot-specific extensions: | |
| 72 | + | Standard MCP config — same as Claude Desktop, with zut-specific extensions: | |
| 73 | 73 | ||
| 74 | 74 | ```jsonc | |
| 75 | 75 | { | |
| @@ -154,7 +154,7 @@ Standard MCP config — same as Claude Desktop, with zot-specific extensions: | |||
| 154 | 154 | ||
| 155 | 155 | ``` | |
| 156 | 156 | ┌──────────────────────────────────────────────────────────────┐ | |
| 157 | - | │ zot agent │ | |
| 157 | + | │ zut agent │ | |
| 158 | 158 | │ │ | |
| 159 | 159 | │ ┌──────────┐ tool_call ┌──────────────┐ │ | |
| 160 | 160 | │ │ LLM │───────────────▶│ mcp-bridge │ │ | |
| @@ -173,7 +173,7 @@ Standard MCP config — same as Claude Desktop, with zot-specific extensions: | |||
| 173 | 173 | ||
| 174 | 174 | 1. **Startup**: mcp-bridge reads config and registers tools from `mcp-tools-cache.json` | |
| 175 | 175 | 2. **Background refresh**: starts configured MCP servers, calls `tools/list`, and updates the cache when definitions change | |
| 176 | - | 3. **Reload**: if the cache changed, run `/reload-ext` once so zot rebuilds the tool registry with the new definitions | |
| 176 | + | 3. **Reload**: if the cache changed, run `/reload-ext` once so zut rebuilds the tool registry with the new definitions | |
| 177 | 177 | 4. **Naming**: tools appear as `mcp__<server>__<tool>` (e.g., `mcp__filesystem__read_file`) | |
| 178 | 178 | 5. **Idle timeout**: servers not used for 5 minutes are automatically stopped | |
| 179 | 179 | 6. **Auto-respawn**: calling a tool on a stopped server wakes it up | |
| @@ -196,7 +196,7 @@ Standard MCP config — same as Claude Desktop, with zot-specific extensions: | |||
| 196 | 196 | ||
| 197 | 197 | ## Tool Naming | |
| 198 | 198 | ||
| 199 | - | Tools are namespaced to avoid collisions with zot's built-in tools: | |
| 199 | + | Tools are namespaced to avoid collisions with zut's built-in tools: | |
| 200 | 200 | ||
| 201 | 201 | ``` | |
| 202 | 202 | mcp__<server>__<tool> | |
| @@ -214,7 +214,7 @@ Server and tool names are sanitized (non-alphanumeric characters become `_`). | |||
| 214 | 214 | ||
| 215 | 215 | The bridge uses a "smart lazy" strategy: | |
| 216 | 216 | ||
| 217 | - | 1. **On startup**: cached tool definitions are registered without blocking zot startup | |
| 217 | + | 1. **On startup**: cached tool definitions are registered without blocking zut startup | |
| 218 | 218 | 2. **In the background**: servers start long enough to refresh the tool cache | |
| 219 | 219 | 3. **During use**: servers stay running for fast tool calls | |
| 220 | 220 | 4. **After 5 min idle**: unused servers are automatically stopped (saves memory/CPU) | |
| @@ -235,7 +235,7 @@ This gives you: | |||
| 235 | 235 | ||
| 236 | 236 | **View extension logs:** | |
| 237 | 237 | ```bash | |
| 238 | - | zot ext logs mcp -f | |
| 238 | + | zut ext logs mcp -f | |
| 239 | 239 | ``` | |
| 240 | 240 | ||
| 241 | 241 | **Common issues:** | |
| @@ -261,11 +261,11 @@ go build -o mcp-bridge . | |||
| 261 | 261 | go test ./... | |
| 262 | 262 | go vet ./... | |
| 263 | 263 | ||
| 264 | - | # Run without installing (for one zot session) | |
| 265 | - | zot --ext . | |
| 264 | + | # Run without installing (for one zut session) | |
| 265 | + | zut --ext . | |
| 266 | 266 | ||
| 267 | 267 | # View logs | |
| 268 | - | zot ext logs mcp -f | |
| 268 | + | zut ext logs mcp -f | |
| 269 | 269 | ``` | |
| 270 | 270 | ||
| 271 | 271 | ## License | |
M examples/extensions/mcp-bridge/bridge.go +30 -30
| @@ -1,11 +1,11 @@ | |||
| 1 | - | // bridge.go — MCP tool → zot tool registration and routing. | |
| 1 | + | // bridge.go — MCP tool → zut tool registration and routing. | |
| 2 | 2 | // | |
| 3 | - | // Converts MCP tools into zot-registered tools with namespaced names: | |
| 3 | + | // Converts MCP tools into zut-registered tools with namespaced names: | |
| 4 | 4 | // | |
| 5 | 5 | // mcp__<server>__<tool> | |
| 6 | 6 | // | |
| 7 | 7 | // The double underscore separates the server name from the tool name, | |
| 8 | - | // avoiding collisions with zot's built-in tools (read, write, edit, bash, lsp, skill). | |
| 8 | + | // avoiding collisions with zut's built-in tools (read, write, edit, bash, lsp, skill). | |
| 9 | 9 | package main | |
| 10 | 10 | ||
| 11 | 11 | import ( | |
| @@ -20,8 +20,8 @@ import ( | |||
| 20 | 20 | "sync" | |
| 21 | 21 | "time" | |
| 22 | 22 | ||
| 23 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 23 | 24 | "github.com/mark3labs/mcp-go/mcp" | |
| 24 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 25 | 25 | ) | |
| 26 | 26 | ||
| 27 | 27 | const ( | |
| @@ -48,14 +48,14 @@ var mcpSearchToolSchema = json.RawMessage(`{ | |||
| 48 | 48 | "additionalProperties": false | |
| 49 | 49 | }`) | |
| 50 | 50 | ||
| 51 | - | // toolMapping tracks which zot tool name maps to which MCP server + tool. | |
| 51 | + | // toolMapping tracks which zut tool name maps to which MCP server + tool. | |
| 52 | 52 | type toolMapping struct { | |
| 53 | 53 | serverName string // e.g. "filesystem" | |
| 54 | 54 | mcpTool string // e.g. "read_file" | |
| 55 | 55 | description string | |
| 56 | 56 | } | |
| 57 | 57 | ||
| 58 | - | // bridge connects MCP servers to zot's extension protocol. | |
| 58 | + | // bridge connects MCP servers to zut's extension protocol. | |
| 59 | 59 | type bridge struct { | |
| 60 | 60 | e *ext.Extension | |
| 61 | 61 | cwd string | |
| @@ -64,14 +64,14 @@ type bridge struct { | |||
| 64 | 64 | // the lock-free reads in handleToolCall and the idle reaper safe. | |
| 65 | 65 | // Do not add or remove entries after startup. | |
| 66 | 66 | servers map[string]*managedServer | |
| 67 | - | mapping map[string]toolMapping // zot tool name → MCP server + tool; guarded by mu | |
| 67 | + | mapping map[string]toolMapping // zut tool name → MCP server + tool; guarded by mu | |
| 68 | 68 | logger *log.Logger | |
| 69 | 69 | ||
| 70 | 70 | mu sync.Mutex // guards mapping and searchRegistered | |
| 71 | 71 | searchRegistered bool | |
| 72 | 72 | } | |
| 73 | 73 | ||
| 74 | - | // newBridge creates a new MCP→zot bridge. | |
| 74 | + | // newBridge creates a new MCP→zut bridge. | |
| 75 | 75 | func newBridge(e *ext.Extension, cwd string, logger *log.Logger) *bridge { | |
| 76 | 76 | return &bridge{ | |
| 77 | 77 | e: e, | |
| @@ -82,8 +82,8 @@ func newBridge(e *ext.Extension, cwd string, logger *log.Logger) *bridge { | |||
| 82 | 82 | } | |
| 83 | 83 | } | |
| 84 | 84 | ||
| 85 | - | // sanitizeName converts a string into a valid zot tool name component. | |
| 86 | - | // Zot tool names must match [a-zA-Z][a-zA-Z0-9_]*. | |
| 85 | + | // sanitizeName converts a string into a valid zut tool name component. | |
| 86 | + | // Zut tool names must match [a-zA-Z][a-zA-Z0-9_]*. | |
| 87 | 87 | var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9]`) | |
| 88 | 88 | ||
| 89 | 89 | func sanitizeName(s string) string { | |
| @@ -98,7 +98,7 @@ func sanitizeName(s string) string { | |||
| 98 | 98 | return s | |
| 99 | 99 | } | |
| 100 | 100 | ||
| 101 | - | // toolName builds the namespaced zot tool name for an MCP tool. | |
| 101 | + | // toolName builds the namespaced zut tool name for an MCP tool. | |
| 102 | 102 | func toolName(serverName, mcpToolName string) string { | |
| 103 | 103 | return fmt.Sprintf("mcp__%s__%s", sanitizeName(serverName), sanitizeName(mcpToolName)) | |
| 104 | 104 | } | |
| @@ -222,29 +222,29 @@ func (b *bridge) refreshToolCache(ctx context.Context, path string) (bool, error | |||
| 222 | 222 | } | |
| 223 | 223 | ||
| 224 | 224 | func (b *bridge) registerCachedTool(serverName string, tool cachedTool) { | |
| 225 | - | zotName := toolName(serverName, tool.Name) | |
| 225 | + | zutName := toolName(serverName, tool.Name) | |
| 226 | 226 | ||
| 227 | 227 | b.mu.Lock() | |
| 228 | - | if existing, exists := b.mapping[zotName]; exists { | |
| 228 | + | if existing, exists := b.mapping[zutName]; exists { | |
| 229 | 229 | b.mu.Unlock() | |
| 230 | 230 | if existing.serverName != serverName || existing.mcpTool != tool.Name { | |
| 231 | 231 | b.logger.Printf("tool name collision: %s already maps to %s/%s; skipping %s/%s", | |
| 232 | - | zotName, existing.serverName, existing.mcpTool, serverName, tool.Name) | |
| 232 | + | zutName, existing.serverName, existing.mcpTool, serverName, tool.Name) | |
| 233 | 233 | } | |
| 234 | 234 | return | |
| 235 | 235 | } | |
| 236 | - | b.mapping[zotName] = toolMapping{ | |
| 236 | + | b.mapping[zutName] = toolMapping{ | |
| 237 | 237 | serverName: serverName, | |
| 238 | 238 | mcpTool: tool.Name, | |
| 239 | 239 | description: tool.Description, | |
| 240 | 240 | } | |
| 241 | 241 | b.mu.Unlock() | |
| 242 | 242 | ||
| 243 | - | b.e.DeferredTool(zotName, tool.Description, json.RawMessage(tool.Schema), func(args json.RawMessage) ext.ToolResult { | |
| 244 | - | return b.handleToolCall(zotName, args) | |
| 243 | + | b.e.DeferredTool(zutName, tool.Description, json.RawMessage(tool.Schema), func(args json.RawMessage) ext.ToolResult { | |
| 244 | + | return b.handleToolCall(zutName, args) | |
| 245 | 245 | }) | |
| 246 | 246 | ||
| 247 | - | b.logger.Printf("registered deferred cached tool: %s → %s/%s", zotName, serverName, tool.Name) | |
| 247 | + | b.logger.Printf("registered deferred cached tool: %s → %s/%s", zutName, serverName, tool.Name) | |
| 248 | 248 | } | |
| 249 | 249 | ||
| 250 | 250 | type toolSearchArgs struct { | |
| @@ -326,8 +326,8 @@ func (b *bridge) findTools(query string, limit int) []toolSearchMatch { | |||
| 326 | 326 | return matches | |
| 327 | 327 | } | |
| 328 | 328 | ||
| 329 | - | func scoreToolMatch(terms []string, zotName string, mapping toolMapping) int { | |
| 330 | - | name := strings.ToLower(zotName) | |
| 329 | + | func scoreToolMatch(terms []string, zutName string, mapping toolMapping) int { | |
| 330 | + | name := strings.ToLower(zutName) | |
| 331 | 331 | server := strings.ToLower(mapping.serverName) | |
| 332 | 332 | mcpName := strings.ToLower(mapping.mcpTool) | |
| 333 | 333 | description := strings.ToLower(mapping.description) | |
| @@ -408,17 +408,17 @@ func mcpToolSchema(tool mcp.Tool) any { | |||
| 408 | 408 | return schema | |
| 409 | 409 | } | |
| 410 | 410 | ||
| 411 | - | // handleToolCall routes a zot tool call to the appropriate MCP server. | |
| 412 | - | func (b *bridge) handleToolCall(zotName string, args json.RawMessage) ext.ToolResult { | |
| 411 | + | // handleToolCall routes a zut tool call to the appropriate MCP server. | |
| 412 | + | func (b *bridge) handleToolCall(zutName string, args json.RawMessage) ext.ToolResult { | |
| 413 | 413 | b.mu.Lock() | |
| 414 | - | mapping, ok := b.mapping[zotName] | |
| 414 | + | mapping, ok := b.mapping[zutName] | |
| 415 | 415 | b.mu.Unlock() | |
| 416 | 416 | ||
| 417 | 417 | if !ok { | |
| 418 | 418 | return ext.TextErrorResult(fmt.Sprintf( | |
| 419 | 419 | "Tool '%s' not found. This tool was registered but is no longer available. "+ | |
| 420 | 420 | "The MCP server may have been stopped. Try running '/mcp' to check server status.", | |
| 421 | - | zotName)) | |
| 421 | + | zutName)) | |
| 422 | 422 | } | |
| 423 | 423 | ||
| 424 | 424 | srv, ok := b.servers[mapping.serverName] | |
| @@ -449,8 +449,8 @@ func (b *bridge) handleToolCall(zotName string, args json.RawMessage) ext.ToolRe | |||
| 449 | 449 | err, mapping.serverName, mapping.mcpTool, mapping.serverName)) | |
| 450 | 450 | } | |
| 451 | 451 | ||
| 452 | - | // Convert MCP result to zot ToolResult | |
| 453 | - | return mcpResultToZot(result) | |
| 452 | + | // Convert MCP result to zut ToolResult | |
| 453 | + | return mcpResultToZut(result) | |
| 454 | 454 | } | |
| 455 | 455 | ||
| 456 | 456 | // timeoutErrorMessage formats the user-facing text for a deadline error. | |
| @@ -461,8 +461,8 @@ func timeoutErrorMessage(serverName string, requestTimeout int) string { | |||
| 461 | 461 | requestTimeout, serverName) | |
| 462 | 462 | } | |
| 463 | 463 | ||
| 464 | - | // mcpResultToZot converts an MCP CallToolResult to a zot ToolResult. | |
| 465 | - | func mcpResultToZot(result *mcp.CallToolResult) ext.ToolResult { | |
| 464 | + | // mcpResultToZut converts an MCP CallToolResult to a zut ToolResult. | |
| 465 | + | func mcpResultToZut(result *mcp.CallToolResult) ext.ToolResult { | |
| 466 | 466 | if result == nil { | |
| 467 | 467 | return ext.TextResult("(no result)") | |
| 468 | 468 | } | |
| @@ -492,7 +492,7 @@ func mcpResultToZot(result *mcp.CallToolResult) ext.ToolResult { | |||
| 492 | 492 | return tr | |
| 493 | 493 | } | |
| 494 | 494 | ||
| 495 | - | // startAll starts all configured servers without re-registering their tools with zot. | |
| 495 | + | // startAll starts all configured servers without re-registering their tools with zut. | |
| 496 | 496 | func (b *bridge) startAll(ctx context.Context) error { | |
| 497 | 497 | var wg sync.WaitGroup | |
| 498 | 498 | errCh := make(chan error, len(b.servers)) | |
| @@ -523,7 +523,7 @@ func (b *bridge) startAll(ctx context.Context) error { | |||
| 523 | 523 | ||
| 524 | 524 | // startIdleReaper runs a background goroutine that kills idle servers. | |
| 525 | 525 | // It has no shutdown mechanism by design: it lives exactly as long as | |
| 526 | - | // the extension process, and zot reaps the whole process on exit. | |
| 526 | + | // the extension process, and zut reaps the whole process on exit. | |
| 527 | 527 | func (b *bridge) startIdleReaper() { | |
| 528 | 528 | go func() { | |
| 529 | 529 | ticker := time.NewTicker(30 * time.Second) | |
M examples/extensions/mcp-bridge/cache.go +1 -1
| @@ -32,7 +32,7 @@ type toolCache struct { | |||
| 32 | 32 | } | |
| 33 | 33 | ||
| 34 | 34 | func toolCachePath() string { | |
| 35 | - | return filepath.Join(zotHome(), "mcp-tools-cache.json") | |
| 35 | + | return filepath.Join(zutHome(), "mcp-tools-cache.json") | |
| 36 | 36 | } | |
| 37 | 37 | ||
| 38 | 38 | func serverFingerprint(cfg ServerConfig) string { | |
M examples/extensions/mcp-bridge/config.go +11 -11
| @@ -3,8 +3,8 @@ | |||
| 3 | 3 | // Reads standard MCP config files (same format as Claude Desktop, Cursor, etc.) | |
| 4 | 4 | // from two locations: | |
| 5 | 5 | // | |
| 6 | - | // 1. Global: $ZOT_HOME/mcp.json | |
| 7 | - | // 2. Project: .zot/mcp.json (in the current working directory) | |
| 6 | + | // 1. Global: $ZUT_HOME/mcp.json | |
| 7 | + | // 2. Project: .zut/mcp.json (in the current working directory) | |
| 8 | 8 | // | |
| 9 | 9 | // Project config overrides global config per-server (shallow merge). | |
| 10 | 10 | package main | |
| @@ -42,23 +42,23 @@ type Config struct { | |||
| 42 | 42 | MCPServers map[string]ServerConfig `json:"mcpServers"` | |
| 43 | 43 | } | |
| 44 | 44 | ||
| 45 | - | // zotHome returns the zot state directory. | |
| 46 | - | func zotHome() string { | |
| 47 | - | if h := os.Getenv("ZOT_HOME"); h != "" { | |
| 45 | + | // zutHome returns the zut state directory. | |
| 46 | + | func zutHome() string { | |
| 47 | + | if h := os.Getenv("ZUT_HOME"); h != "" { | |
| 48 | 48 | return h | |
| 49 | 49 | } | |
| 50 | 50 | if xdg := os.Getenv("XDG_STATE_HOME"); xdg != "" { | |
| 51 | - | return filepath.Join(xdg, "zot") | |
| 51 | + | return filepath.Join(xdg, "zut") | |
| 52 | 52 | } | |
| 53 | 53 | switch runtime.GOOS { | |
| 54 | 54 | case "darwin": | |
| 55 | 55 | home, _ := os.UserHomeDir() | |
| 56 | - | return filepath.Join(home, "Library", "Application Support", "zot") | |
| 56 | + | return filepath.Join(home, "Library", "Application Support", "zut") | |
| 57 | 57 | case "windows": | |
| 58 | - | return filepath.Join(os.Getenv("APPDATA"), "zot") | |
| 58 | + | return filepath.Join(os.Getenv("APPDATA"), "zut") | |
| 59 | 59 | default: // linux, freebsd, etc. | |
| 60 | 60 | home, _ := os.UserHomeDir() | |
| 61 | - | return filepath.Join(home, ".local", "state", "zot") | |
| 61 | + | return filepath.Join(home, ".local", "state", "zut") | |
| 62 | 62 | } | |
| 63 | 63 | } | |
| 64 | 64 | ||
| @@ -68,14 +68,14 @@ func loadConfig(cwd string) (Config, error) { | |||
| 68 | 68 | cfg := Config{MCPServers: make(map[string]ServerConfig)} | |
| 69 | 69 | ||
| 70 | 70 | // 1. Global config | |
| 71 | - | globalPath := filepath.Join(zotHome(), "mcp.json") | |
| 71 | + | globalPath := filepath.Join(zutHome(), "mcp.json") | |
| 72 | 72 | if err := mergeConfig(&cfg, globalPath); err != nil && !errors.Is(err, fs.ErrNotExist) { | |
| 73 | 73 | return cfg, fmt.Errorf("global config %s: %w", globalPath, err) | |
| 74 | 74 | } | |
| 75 | 75 | ||
| 76 | 76 | // 2. Project config (overrides global per-server) | |
| 77 | 77 | if cwd != "" { | |
| 78 | - | projectPath := filepath.Join(cwd, ".zot", "mcp.json") | |
| 78 | + | projectPath := filepath.Join(cwd, ".zut", "mcp.json") | |
| 79 | 79 | if err := mergeConfig(&cfg, projectPath); err != nil && !errors.Is(err, fs.ErrNotExist) { | |
| 80 | 80 | return cfg, fmt.Errorf("project config %s: %w", projectPath, err) | |
| 81 | 81 | } | |
M examples/extensions/mcp-bridge/config_test.go +4 -4
| @@ -19,7 +19,7 @@ func writeJSON(t *testing.T, path, content string) { | |||
| 19 | 19 | func TestLoadConfigProjectOverridesGlobal(t *testing.T) { | |
| 20 | 20 | home := t.TempDir() | |
| 21 | 21 | proj := t.TempDir() | |
| 22 | - | t.Setenv("ZOT_HOME", home) | |
| 22 | + | t.Setenv("ZUT_HOME", home) | |
| 23 | 23 | ||
| 24 | 24 | writeJSON(t, filepath.Join(home, "mcp.json"), `{ | |
| 25 | 25 | "mcpServers": { | |
| @@ -27,7 +27,7 @@ func TestLoadConfigProjectOverridesGlobal(t *testing.T) { | |||
| 27 | 27 | "global-only": {"command": "g"} | |
| 28 | 28 | } | |
| 29 | 29 | }`) | |
| 30 | - | writeJSON(t, filepath.Join(proj, ".zot", "mcp.json"), `{ | |
| 30 | + | writeJSON(t, filepath.Join(proj, ".zut", "mcp.json"), `{ | |
| 31 | 31 | "mcpServers": { | |
| 32 | 32 | "shared": {"command": "project-cmd"} | |
| 33 | 33 | } | |
| @@ -50,7 +50,7 @@ func TestLoadConfigProjectOverridesGlobal(t *testing.T) { | |||
| 50 | 50 | } | |
| 51 | 51 | ||
| 52 | 52 | func TestLoadConfigMissingFilesIsNotAnError(t *testing.T) { | |
| 53 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 53 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 54 | 54 | cfg, err := loadConfig(t.TempDir()) | |
| 55 | 55 | if err != nil { | |
| 56 | 56 | t.Fatalf("loadConfig with no files: %v", err) | |
| @@ -61,7 +61,7 @@ func TestLoadConfigMissingFilesIsNotAnError(t *testing.T) { | |||
| 61 | 61 | } | |
| 62 | 62 | ||
| 63 | 63 | func TestHandleSetupProjectRequiresCwd(t *testing.T) { | |
| 64 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 64 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 65 | 65 | if _, err := handleSetup([]string{"add", "grep", "--project"}, ""); err == nil { | |
| 66 | 66 | t.Fatal("expected error for --project with unknown working directory") | |
| 67 | 67 | } | |
M examples/extensions/mcp-bridge/deferred_tools_test.go +2 -2
| @@ -10,8 +10,8 @@ import ( | |||
| 10 | 10 | "testing" | |
| 11 | 11 | "time" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 14 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 13 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 14 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | type bridgeExtensionHarness struct { | |
M examples/extensions/mcp-bridge/extension.json +1 -1
| @@ -3,6 +3,6 @@ | |||
| 3 | 3 | "version": "1.1.0", | |
| 4 | 4 | "exec": "./mcp-bridge", | |
| 5 | 5 | "language": "go", | |
| 6 | - | "description": "Connect zot to MCP servers with on-demand tool discovery", | |
| 6 | + | "description": "Connect zut to MCP servers with on-demand tool discovery", | |
| 7 | 7 | "enabled": true | |
| 8 | 8 | } | |
M examples/extensions/mcp-bridge/fsutil_test.go +1 -1
| @@ -80,7 +80,7 @@ func TestToolCacheVersionMismatchInvalidates(t *testing.T) { | |||
| 80 | 80 | ||
| 81 | 81 | func TestConfigFileWrittenOwnerOnly(t *testing.T) { | |
| 82 | 82 | tmp := t.TempDir() | |
| 83 | - | t.Setenv("ZOT_HOME", tmp) | |
| 83 | + | t.Setenv("ZUT_HOME", tmp) | |
| 84 | 84 | ||
| 85 | 85 | if _, err := handleSetup([]string{"add", "grep"}, tmp); err != nil { | |
| 86 | 86 | t.Fatalf("handleSetup: %v", err) | |
M examples/extensions/mcp-bridge/go.mod +4 -4
| @@ -1,12 +1,12 @@ | |||
| 1 | - | module github.com/patriceckhart/zot/examples/extensions/mcp-bridge | |
| 1 | + | module github.com/bnema/zut/examples/extensions/mcp-bridge | |
| 2 | 2 | ||
| 3 | - | go 1.25.5 | |
| 3 | + | go 1.26 | |
| 4 | 4 | ||
| 5 | - | replace github.com/patriceckhart/zot => ../../.. | |
| 5 | + | replace github.com/bnema/zut => ../../.. | |
| 6 | 6 | ||
| 7 | 7 | require ( | |
| 8 | + | github.com/bnema/zut v0.1.0 | |
| 8 | 9 | github.com/mark3labs/mcp-go v0.55.1 | |
| 9 | - | github.com/patriceckhart/zot v0.2.47 | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | require ( | |
M examples/extensions/mcp-bridge/main.go +10 -10
| @@ -1,12 +1,12 @@ | |||
| 1 | - | // mcp-bridge — Connect zot to MCP (Model Context Protocol) servers. | |
| 1 | + | // mcp-bridge — Connect zut to MCP (Model Context Protocol) servers. | |
| 2 | 2 | // | |
| 3 | 3 | // This extension reads MCP server configurations from standard locations | |
| 4 | 4 | // (same format as Claude Desktop, Cursor, etc.) and bridges their tools | |
| 5 | - | // into zot so the LLM can call them. | |
| 5 | + | // into zut so the LLM can call them. | |
| 6 | 6 | // | |
| 7 | 7 | // Config locations: | |
| 8 | - | // - Global: $ZOT_HOME/mcp.json | |
| 9 | - | // - Project: .zot/mcp.json | |
| 8 | + | // - Global: $ZUT_HOME/mcp.json | |
| 9 | + | // - Project: .zut/mcp.json | |
| 10 | 10 | // | |
| 11 | 11 | // Smart lazy: servers are spawned on startup to discover tools, then | |
| 12 | 12 | // killed after 5 minutes of idle time. On the next tool call, they're | |
| @@ -30,7 +30,7 @@ | |||
| 30 | 30 | // | |
| 31 | 31 | // Install: | |
| 32 | 32 | // | |
| 33 | - | // zot ext install . | |
| 33 | + | // zut ext install . | |
| 34 | 34 | package main | |
| 35 | 35 | ||
| 36 | 36 | import ( | |
| @@ -41,13 +41,13 @@ import ( | |||
| 41 | 41 | "strings" | |
| 42 | 42 | "time" | |
| 43 | 43 | ||
| 44 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 44 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 45 | 45 | ) | |
| 46 | 46 | ||
| 47 | 47 | func main() { | |
| 48 | 48 | e := ext.New("mcp", "1.1.0") | |
| 49 | 49 | ||
| 50 | - | // Logger writes to stderr (captured by zot into ext logs) | |
| 50 | + | // Logger writes to stderr (captured by zut into ext logs) | |
| 51 | 51 | logger := log.New(os.Stderr, "[mcp-bridge] ", log.LstdFlags) | |
| 52 | 52 | ||
| 53 | 53 | var b *bridge | |
| @@ -105,7 +105,7 @@ func main() { | |||
| 105 | 105 | } | |
| 106 | 106 | ||
| 107 | 107 | func startBackgroundToolRefresh(e *ext.Extension, b *bridge, logger *log.Logger, cachePath string, cachedToolCount int) { | |
| 108 | - | // Refresh discovery in the background so zot startup is not blocked. Only ask | |
| 108 | + | // Refresh discovery in the background so zut startup is not blocked. Only ask | |
| 109 | 109 | // for /reload-ext if the discovered tool cache actually changed; otherwise a | |
| 110 | 110 | // reload would just repeat this cycle without adding anything. | |
| 111 | 111 | go func() { | |
| @@ -216,8 +216,8 @@ func mcpHelp(b *bridge) string { | |||
| 216 | 216 | sb.WriteString(" /mcp setup templates List setup templates\n") | |
| 217 | 217 | sb.WriteString(" /mcp setup add <template> [options] Add a server template\n") | |
| 218 | 218 | sb.WriteString("\nSetup options\n") | |
| 219 | - | sb.WriteString(" --global Write to $ZOT_HOME/mcp.json (default)\n") | |
| 220 | - | sb.WriteString(" --project Write to .zot/mcp.json\n") | |
| 219 | + | sb.WriteString(" --global Write to $ZUT_HOME/mcp.json (default)\n") | |
| 220 | + | sb.WriteString(" --project Write to .zut/mcp.json\n") | |
| 221 | 221 | sb.WriteString(" --name <server-name> Use a custom configured server name\n") | |
| 222 | 222 | return strings.TrimRight(sb.String(), "\n") | |
| 223 | 223 | } | |
M examples/extensions/mcp-bridge/server.go +2 -2
| @@ -157,7 +157,7 @@ func (s *managedServer) doStart(ctx context.Context) (*client.Client, []mcp.Tool | |||
| 157 | 157 | Params: mcp.InitializeParams{ | |
| 158 | 158 | ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION, | |
| 159 | 159 | ClientInfo: mcp.Implementation{ | |
| 160 | - | Name: "zot-mcp-bridge", | |
| 160 | + | Name: "zut-mcp-bridge", | |
| 161 | 161 | Version: "1.0.0", | |
| 162 | 162 | }, | |
| 163 | 163 | Capabilities: mcp.ClientCapabilities{}, | |
| @@ -191,7 +191,7 @@ func (s *managedServer) startStdio() (*client.Client, error) { | |||
| 191 | 191 | } | |
| 192 | 192 | ||
| 193 | 193 | // Create stdio client (this spawns the subprocess). Run stdio MCP | |
| 194 | - | // servers from the zot session's project directory, not the installed | |
| 194 | + | // servers from the zut session's project directory, not the installed | |
| 195 | 195 | // extension directory. Many MCP servers use their process cwd as their | |
| 196 | 196 | // project root when answering relative-path or directory-listing | |
| 197 | 197 | // requests, so inheriting mcp-bridge's cwd makes them operate on the | |
M examples/extensions/mcp-bridge/setup.go +4 -4
| @@ -75,7 +75,7 @@ func setupHelp() string { | |||
| 75 | 75 | ||
| 76 | 76 | var b strings.Builder | |
| 77 | 77 | b.WriteString("mcp-bridge setup\n\n") | |
| 78 | - | b.WriteString("Add a known MCP server template to your zot MCP config.\n\n") | |
| 78 | + | b.WriteString("Add a known MCP server template to your zut MCP config.\n\n") | |
| 79 | 79 | b.WriteString("Usage:\n") | |
| 80 | 80 | b.WriteString(" /mcp setup add <template> [--global|--project] [--name <server-name>]\n") | |
| 81 | 81 | b.WriteString(" /mcp setup templates\n\n") | |
| @@ -88,7 +88,7 @@ func setupHelp() string { | |||
| 88 | 88 | b.WriteString(" /mcp setup add grep\n") | |
| 89 | 89 | b.WriteString(" /mcp setup add filesystem --project\n") | |
| 90 | 90 | b.WriteString(" /mcp setup add context7 --global --name docs\n") | |
| 91 | - | b.WriteString("\nDefault target is global: $ZOT_HOME/mcp.json. Run /reload-ext after changes.\n") | |
| 91 | + | b.WriteString("\nDefault target is global: $ZUT_HOME/mcp.json. Run /reload-ext after changes.\n") | |
| 92 | 92 | return b.String() | |
| 93 | 93 | } | |
| 94 | 94 | ||
| @@ -132,12 +132,12 @@ func handleSetup(args []string, cwd string) (string, error) { | |||
| 132 | 132 | } | |
| 133 | 133 | } | |
| 134 | 134 | ||
| 135 | - | path := filepath.Join(zotHome(), "mcp.json") | |
| 135 | + | path := filepath.Join(zutHome(), "mcp.json") | |
| 136 | 136 | if target == "project" { | |
| 137 | 137 | if cwd == "" { | |
| 138 | 138 | return "", fmt.Errorf("--project requires a working directory, but none is known") | |
| 139 | 139 | } | |
| 140 | - | path = filepath.Join(cwd, ".zot", "mcp.json") | |
| 140 | + | path = filepath.Join(cwd, ".zut", "mcp.json") | |
| 141 | 141 | } | |
| 142 | 142 | ||
| 143 | 143 | cfg, err := readConfigFile(path) | |
M examples/extensions/mcp-bridge/setup_test.go +3 -3
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | 9 | ||
| 10 | 10 | func TestHandleSetupAddGrepGlobal(t *testing.T) { | |
| 11 | 11 | tmp := t.TempDir() | |
| 12 | - | t.Setenv("ZOT_HOME", tmp) | |
| 12 | + | t.Setenv("ZUT_HOME", tmp) | |
| 13 | 13 | ||
| 14 | 14 | out, err := handleSetup([]string{"add", "grep"}, tmp) | |
| 15 | 15 | if err != nil { | |
| @@ -44,7 +44,7 @@ func TestHandleSetupAddFilesystemProject(t *testing.T) { | |||
| 44 | 44 | t.Fatalf("handleSetup add filesystem: %v", err) | |
| 45 | 45 | } | |
| 46 | 46 | ||
| 47 | - | data, err := os.ReadFile(filepath.Join(cwd, ".zot", "mcp.json")) | |
| 47 | + | data, err := os.ReadFile(filepath.Join(cwd, ".zut", "mcp.json")) | |
| 48 | 48 | if err != nil { | |
| 49 | 49 | t.Fatalf("read project mcp.json: %v", err) | |
| 50 | 50 | } | |
| @@ -60,7 +60,7 @@ func TestHandleSetupAddFilesystemProject(t *testing.T) { | |||
| 60 | 60 | ||
| 61 | 61 | func TestHandleSetupDuplicate(t *testing.T) { | |
| 62 | 62 | tmp := t.TempDir() | |
| 63 | - | t.Setenv("ZOT_HOME", tmp) | |
| 63 | + | t.Setenv("ZUT_HOME", tmp) | |
| 64 | 64 | ||
| 65 | 65 | if _, err := handleSetup([]string{"add", "grep"}, tmp); err != nil { | |
| 66 | 66 | t.Fatalf("first add failed: %v", err) | |
M examples/extensions/mcp-bridge/status_test.go +4 -4
| @@ -198,8 +198,8 @@ func TestCompactErrClassifications(t *testing.T) { | |||
| 198 | 198 | } | |
| 199 | 199 | } | |
| 200 | 200 | ||
| 201 | - | func TestMCPResultToZot(t *testing.T) { | |
| 202 | - | if got := mcpResultToZot(nil); len(got.Content) == 0 || got.IsError { | |
| 201 | + | func TestMCPResultToZut(t *testing.T) { | |
| 202 | + | if got := mcpResultToZut(nil); len(got.Content) == 0 || got.IsError { | |
| 203 | 203 | t.Fatalf("nil result: %+v", got) | |
| 204 | 204 | } | |
| 205 | 205 | ||
| @@ -207,7 +207,7 @@ func TestMCPResultToZot(t *testing.T) { | |||
| 207 | 207 | Content: []mcp.Content{mcp.TextContent{Type: "text", Text: "hello"}}, | |
| 208 | 208 | IsError: true, | |
| 209 | 209 | } | |
| 210 | - | got := mcpResultToZot(res) | |
| 210 | + | got := mcpResultToZut(res) | |
| 211 | 211 | if !got.IsError { | |
| 212 | 212 | t.Fatal("IsError not propagated") | |
| 213 | 213 | } | |
| @@ -215,7 +215,7 @@ func TestMCPResultToZot(t *testing.T) { | |||
| 215 | 215 | t.Fatalf("content count = %d, want 1", len(got.Content)) | |
| 216 | 216 | } | |
| 217 | 217 | ||
| 218 | - | empty := mcpResultToZot(&mcp.CallToolResult{}) | |
| 218 | + | empty := mcpResultToZut(&mcp.CallToolResult{}) | |
| 219 | 219 | if len(empty.Content) == 0 { | |
| 220 | 220 | t.Fatal("empty MCP result must produce placeholder content") | |
| 221 | 221 | } | |
M examples/extensions/scratchpad/README.md +6 -6
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # scratchpad — example zot extension (TypeScript, source-run) | |
| 1 | + | # scratchpad — example zut extension (TypeScript, source-run) | |
| 2 | 2 | ||
| 3 | 3 | Real `.ts` (not `.js`), no build step, no SDK. Runs via `npx -y tsx | |
| 4 | 4 | ./index.ts`, which downloads `tsx` into npm's cache on first | |
| @@ -20,14 +20,14 @@ Node 18+ and `npx` (bundled with npm). | |||
| 20 | 20 | From this directory: | |
| 21 | 21 | ||
| 22 | 22 | ```bash | |
| 23 | - | zot ext install . | |
| 23 | + | zut ext install . | |
| 24 | 24 | ``` | |
| 25 | 25 | ||
| 26 | - | This copies the manifest + source into `$ZOT_HOME/extensions/scratchpad/`. | |
| 26 | + | This copies the manifest + source into `$ZUT_HOME/extensions/scratchpad/`. | |
| 27 | 27 | ||
| 28 | 28 | ## Use | |
| 29 | 29 | ||
| 30 | - | In zot: | |
| 30 | + | In zut: | |
| 31 | 31 | ||
| 32 | 32 | - `/note remind me to update the changelog` — appends to the scratchpad | |
| 33 | 33 | - `/notes` — shows everything stored | |
| @@ -41,8 +41,8 @@ The model also has a `read_notes` tool. Ask it: | |||
| 41 | 41 | ||
| 42 | 42 | ## Storage | |
| 43 | 43 | ||
| 44 | - | Notes persist as JSONL at `<cwd>/.zot/scratchpad-notes.jsonl`. The | |
| 45 | - | file is created on first `/note` and survives zot restarts. Each line | |
| 44 | + | Notes persist as JSONL at `<cwd>/.zut/scratchpad-notes.jsonl`. The | |
| 45 | + | file is created on first `/note` and survives zut restarts. Each line | |
| 46 | 46 | is one note: `{"at":"2026-04-19T13:00:00.000Z","text":"..."}`. | |
| 47 | 47 | ||
| 48 | 48 | Scope is per-project: switching to a different cwd gives you a | |
M examples/extensions/scratchpad/index.ts +17 -10
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // scratchpad — a real .ts zot extension with no SDK and no build step. | |
| 1 | + | // scratchpad — a real .ts zut extension with no SDK and no build step. | |
| 2 | 2 | // | |
| 3 | 3 | // Runs via `npx --yes tsx index.ts` (declared in extension.json). | |
| 4 | 4 | // First invocation downloads tsx into npm's cache; every subsequent | |
| @@ -47,7 +47,7 @@ interface ToolCall { | |||
| 47 | 47 | interface HelloAck { | |
| 48 | 48 | type: "hello_ack"; | |
| 49 | 49 | protocol_version: number; | |
| 50 | - | zot_version: string; | |
| 50 | + | zut_version: string; | |
| 51 | 51 | provider: string; | |
| 52 | 52 | model: string; | |
| 53 | 53 | cwd: string; | |
| @@ -89,23 +89,23 @@ function send(frame: Frame): void { | |||
| 89 | 89 | } | |
| 90 | 90 | ||
| 91 | 91 | function log(msg: string): void { | |
| 92 | - | // stderr is captured by zot to $ZOT_HOME/logs/ext-<name>.log; | |
| 92 | + | // stderr is captured by zut to $ZUT_HOME/logs/ext-<name>.log; | |
| 93 | 93 | // safe for debug output. stdout is reserved for the protocol. | |
| 94 | 94 | stderr.write(`[${NAME}] ${msg}\n`); | |
| 95 | 95 | } | |
| 96 | 96 | ||
| 97 | 97 | // ---- the scratchpad state itself ---- | |
| 98 | 98 | // | |
| 99 | - | // Notes persist as JSONL under <cwd>/.zot/scratchpad-notes.jsonl so | |
| 100 | - | // they survive zot restarts and stay scoped to the project. The path | |
| 99 | + | // Notes persist as JSONL under <cwd>/.zut/scratchpad-notes.jsonl so | |
| 100 | + | // they survive zut restarts and stay scoped to the project. The path | |
| 101 | 101 | // is resolved once HelloAck arrives (which carries cwd); until then | |
| 102 | 102 | // notesPath is empty and reads/writes no-op safely. | |
| 103 | 103 | // | |
| 104 | 104 | // One note per line, format: {"at":"<iso>","text":"<body>"} | |
| 105 | 105 | // Append-only on /note; full rewrite on /clear-notes. | |
| 106 | 106 | // | |
| 107 | - | // Single-writer assumption: only one zot session per cwd at a time. | |
| 108 | - | // Concurrent writes from two zot instances would interleave but not | |
| 107 | + | // Single-writer assumption: only one zut session per cwd at a time. | |
| 108 | + | // Concurrent writes from two zut instances would interleave but not | |
| 109 | 109 | // corrupt JSONL line boundaries on POSIX (writes ≤ PIPE_BUF are | |
| 110 | 110 | // atomic). Good enough for a demo. | |
| 111 | 111 | ||
| @@ -115,7 +115,7 @@ let notes: Note[] = []; | |||
| 115 | 115 | let notesPath = ""; | |
| 116 | 116 | ||
| 117 | 117 | function setNotesPath(cwd: string): void { | |
| 118 | - | notesPath = join(cwd, ".zot", "scratchpad-notes.jsonl"); | |
| 118 | + | notesPath = join(cwd, ".zut", "scratchpad-notes.jsonl"); | |
| 119 | 119 | loadNotes(); | |
| 120 | 120 | } | |
| 121 | 121 | ||
| @@ -177,6 +177,7 @@ function renderNotes(): string { | |||
| 177 | 177 | ||
| 178 | 178 | send({ | |
| 179 | 179 | type: "hello", | |
| 180 | + | protocol_version: 2, | |
| 180 | 181 | name: NAME, | |
| 181 | 182 | version: VERSION, | |
| 182 | 183 | capabilities: ["commands", "tools"], | |
| @@ -209,7 +210,7 @@ send({ | |||
| 209 | 210 | }, | |
| 210 | 211 | }); | |
| 211 | 212 | ||
| 212 | - | // Sentinel: tells zot all initial registrations are flushed so the | |
| 213 | + | // Sentinel: tells zut all initial registrations are flushed so the | |
| 213 | 214 | // agent's tool registry can be built without racing the read loop. | |
| 214 | 215 | send({ type: "ready" }); | |
| 215 | 216 | ||
| @@ -251,8 +252,14 @@ rl.on("close", () => { | |||
| 251 | 252 | }); | |
| 252 | 253 | ||
| 253 | 254 | function handleHelloAck(ack: HelloAck): void { | |
| 255 | + | if (ack.protocol_version !== 2) { | |
| 256 | + | log(`unsupported protocol version: ${ack.protocol_version}`); | |
| 257 | + | process.exitCode = 1; | |
| 258 | + | rl.close(); | |
| 259 | + | return; | |
| 260 | + | } | |
| 254 | 261 | log( | |
| 255 | - | `connected to zot ${ack.zot_version} ` + | |
| 262 | + | `connected to zut ${ack.zut_version} ` + | |
| 256 | 263 | `(${ack.provider}/${ack.model}, cwd=${ack.cwd})`, | |
| 257 | 264 | ); | |
| 258 | 265 | if (ack.cwd) setNotesPath(ack.cwd); | |
M examples/extensions/secret/README.md +3 -3
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # secret — example zot extension | |
| 1 | + | # secret — example zut extension | |
| 2 | 2 | ||
| 3 | 3 | Demonstrates **secret collection via a masked panel**: the model asks | |
| 4 | 4 | for a resource that needs a credential, the extension collects the | |
| @@ -50,12 +50,12 @@ go build -o secret . | |||
| 50 | 50 | ## Install | |
| 51 | 51 | ||
| 52 | 52 | ```bash | |
| 53 | - | zot ext install . | |
| 53 | + | zut ext install . | |
| 54 | 54 | ``` | |
| 55 | 55 | ||
| 56 | 56 | ## Try it | |
| 57 | 57 | ||
| 58 | - | In zot, ask: | |
| 58 | + | In zut, ask: | |
| 59 | 59 | ||
| 60 | 60 | > Fetch https://internal.example.com/report — it needs a password. | |
| 61 | 61 | ||
M examples/extensions/secret/main.go +3 -3
| @@ -20,9 +20,9 @@ | |||
| 20 | 20 | // | |
| 21 | 21 | // Install: | |
| 22 | 22 | // | |
| 23 | - | // zot ext install . | |
| 23 | + | // zut ext install . | |
| 24 | 24 | // | |
| 25 | - | // Try it — ask zot something like: | |
| 25 | + | // Try it — ask zut something like: | |
| 26 | 26 | // | |
| 27 | 27 | // "Fetch https://internal.example.com/report — it needs a password." | |
| 28 | 28 | // | |
| @@ -38,7 +38,7 @@ import ( | |||
| 38 | 38 | "strings" | |
| 39 | 39 | "sync" | |
| 40 | 40 | ||
| 41 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 41 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 42 | 42 | ) | |
| 43 | 43 | ||
| 44 | 44 | const schema = `{ | |
M examples/extensions/tasked-phases/README.md +9 -9
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | # tasked-phases | |
| 2 | 2 | ||
| 3 | - | A zot extension for keeping a spec, phased plan, and checklist in one place. | |
| 3 | + | A zut extension for keeping a spec, phased plan, and checklist in one place. | |
| 4 | 4 | It provides: | |
| 5 | 5 | ||
| 6 | 6 | - the `tasked_phases` tool for persistent spec/phase/task state; | |
| @@ -24,7 +24,7 @@ Successful mutations return one progress line; use `get_status` or | |||
| 24 | 24 | ||
| 25 | 25 | ## Build and run | |
| 26 | 26 | ||
| 27 | - | From the zot repository: | |
| 27 | + | From the zut repository: | |
| 28 | 28 | ||
| 29 | 29 | ```bash | |
| 30 | 30 | cd examples/extensions/tasked-phases | |
| @@ -32,31 +32,31 @@ go build -o tasked-phases . | |||
| 32 | 32 | ||
| 33 | 33 | # One-run development load, launched from the project you want to track: | |
| 34 | 34 | cd /path/to/project | |
| 35 | - | zot --no-ext --ext /path/to/zot/examples/extensions/tasked-phases | |
| 35 | + | zut --no-ext --ext /path/to/zut/examples/extensions/tasked-phases | |
| 36 | 36 | ||
| 37 | 37 | # Or install it globally (run this from the extension directory): | |
| 38 | - | cd /path/to/zot/examples/extensions/tasked-phases | |
| 39 | - | zot ext install --build=go . | |
| 38 | + | cd /path/to/zut/examples/extensions/tasked-phases | |
| 39 | + | zut ext install --build=go . | |
| 40 | 40 | ``` | |
| 41 | 41 | ||
| 42 | 42 | `--build=go` explicitly builds the manifest-declared executable in the staged | |
| 43 | 43 | installation. If an incomplete copy was installed already, remove it with | |
| 44 | - | `zot ext remove tasked-phases --yes` before reinstalling. You can also build | |
| 45 | - | manually with `go build -o tasked-phases .` and then use `zot ext install .`. | |
| 44 | + | `zut ext remove tasked-phases --yes` before reinstalling. You can also build | |
| 45 | + | manually with `go build -o tasked-phases .` and then use `zut ext install .`. | |
| 46 | 46 | ||
| 47 | 47 | Then use `/phases` or ask the model to create a spec and phased checklist with | |
| 48 | 48 | `tasked_phases`. The extension keys its durable state by the host-reported | |
| 49 | 49 | project CWD. | |
| 50 | 50 | ||
| 51 | 51 | The extension bundles its workflow skill under `skills/tasked-phases/`, so | |
| 52 | - | `zot ext install` makes the guidance available automatically. The standalone | |
| 52 | + | `zut ext install` makes the guidance available automatically. The standalone | |
| 53 | 53 | copy at `examples/skills/tasked-phases/SKILL.md` remains useful when you want | |
| 54 | 54 | the same workflow without installing the extension. | |
| 55 | 55 | ||
| 56 | 56 | ## Persistence and turn focus | |
| 57 | 57 | ||
| 58 | 58 | The extension stores one restrictive state file per project CWD as a fallback | |
| 59 | - | for hosts that do not persist sessions. When zot session persistence is | |
| 59 | + | for hosts that do not persist sessions. When zut session persistence is | |
| 60 | 60 | available, the plan is also stored as extension-owned state on each session | |
| 61 | 61 | branch and restored when sessions are opened, switched, or forked. | |
| 62 | 62 | ||
M examples/extensions/tasked-phases/main.go +1 -1
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "os" | |
| 7 | 7 | "sync" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 9 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | const ( | |
M examples/extensions/tasked-phases/main_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 7 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestBuildChromeHidesEmptyPlan(t *testing.T) { | |
M examples/extensions/tasked-phases/state.go +1 -1
| @@ -1043,7 +1043,7 @@ func (s *stateStore) load(dataDir, cwd string) error { | |||
| 1043 | 1043 | } | |
| 1044 | 1044 | ||
| 1045 | 1045 | // activateSession replaces the project fallback with the opaque snapshot | |
| 1046 | - | // supplied by zot for the active session branch. An empty snapshot means the | |
| 1046 | + | // supplied by zut for the active session branch. An empty snapshot means the | |
| 1047 | 1047 | // branch has no extension state yet and therefore starts clean. | |
| 1048 | 1048 | func (s *stateStore) activateSession(raw json.RawMessage) error { | |
| 1049 | 1049 | s.mu.Lock() | |
M examples/extensions/todo/README.md +4 -4
| @@ -1,8 +1,8 @@ | |||
| 1 | - | # todo — example zot extension (Go, interactive panel) | |
| 1 | + | # todo — example zut extension (Go, interactive panel) | |
| 2 | 2 | ||
| 3 | 3 | Demonstrates the interactive panel API plus a companion tool the model | |
| 4 | 4 | can call. The slash command opens a persistent todo panel; the tool lets | |
| 5 | - | zot read and update the same todo list. | |
| 5 | + | zut read and update the same todo list. | |
| 6 | 6 | ||
| 7 | 7 | ## Requirements | |
| 8 | 8 | ||
| @@ -13,7 +13,7 @@ Go 1.22+. | |||
| 13 | 13 | From this directory: | |
| 14 | 14 | ||
| 15 | 15 | ```bash | |
| 16 | - | zot ext install . | |
| 16 | + | zut ext install . | |
| 17 | 17 | ``` | |
| 18 | 18 | ||
| 19 | 19 | The example is configured to run directly from source: | |
| @@ -45,7 +45,7 @@ If you do build it, change `extension.json` back to: | |||
| 45 | 45 | ||
| 46 | 46 | ## Use | |
| 47 | 47 | ||
| 48 | - | In zot: | |
| 48 | + | In zut: | |
| 49 | 49 | ||
| 50 | 50 | - `/todo` opens the panel | |
| 51 | 51 | ||
M examples/extensions/todo/go.mod +4 -4
| @@ -1,7 +1,7 @@ | |||
| 1 | - | module zot-todo-extension | |
| 1 | + | module zut-todo-extension | |
| 2 | 2 | ||
| 3 | - | go 1.22 | |
| 3 | + | go 1.26 | |
| 4 | 4 | ||
| 5 | - | require github.com/patriceckhart/zot v0.0.0 | |
| 5 | + | require github.com/bnema/zut v0.1.0 | |
| 6 | 6 | ||
| 7 | - | replace github.com/patriceckhart/zot => /Users/pat/Developer/zot | |
| 7 | + | replace github.com/bnema/zut => ../../.. | |
M examples/extensions/todo/main.go +1 -1
| @@ -8,7 +8,7 @@ import ( | |||
| 8 | 8 | "strings" | |
| 9 | 9 | "sync" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 11 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | type todoItem struct { | |
D examples/extensions/todo/zot-todo-extension +0 -0 binary file: no text diff
binary file: no text diff
M examples/extensions/weather/README.md +5 -5
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # weather — example zot extension (Go, tool-providing) | |
| 1 | + | # weather — example zut extension (Go, tool-providing) | |
| 2 | 2 | ||
| 3 | 3 | Demonstrates the **tool registration** half of the extension protocol | |
| 4 | 4 | (phase 2). The extension exposes a `weather(city)` tool that the LLM | |
| @@ -15,16 +15,16 @@ go build -o weather . | |||
| 15 | 15 | ## Install | |
| 16 | 16 | ||
| 17 | 17 | ```bash | |
| 18 | - | zot ext install . | |
| 18 | + | zut ext install . | |
| 19 | 19 | ``` | |
| 20 | 20 | ||
| 21 | 21 | Copies the directory (manifest + binary) into | |
| 22 | - | `$ZOT_HOME/extensions/weather/`. zot picks it up the next time you | |
| 22 | + | `$ZUT_HOME/extensions/weather/`. zut picks it up the next time you | |
| 23 | 23 | launch the TUI and registers the tool with the agent. | |
| 24 | 24 | ||
| 25 | 25 | ## Use | |
| 26 | 26 | ||
| 27 | - | In zot, ask: | |
| 27 | + | In zut, ask: | |
| 28 | 28 | ||
| 29 | 29 | - "What's the weather in Berlin?" | |
| 30 | 30 | - "Compare the weather in Tokyo and Reykjavik." | |
| @@ -35,7 +35,7 @@ anything by hand. | |||
| 35 | 35 | ||
| 36 | 36 | ## Add the leading slash to your /help table | |
| 37 | 37 | ||
| 38 | - | The tool also shows up in the system prompt's tool list because zot | |
| 38 | + | The tool also shows up in the system prompt's tool list because zut | |
| 39 | 39 | folds extension tools into the agent's registry at startup. | |
| 40 | 40 | ||
| 41 | 41 | ## See also | |
M examples/extensions/weather/main.go +3 -3
| @@ -10,8 +10,8 @@ | |||
| 10 | 10 | // go build -o weather . | |
| 11 | 11 | // | |
| 12 | 12 | // Then drop it next to its extension.json under | |
| 13 | - | // $ZOT_HOME/extensions/weather/, or run `zot ext install ./weather` | |
| 14 | - | // from this directory. After that, asking zot anything like | |
| 13 | + | // $ZUT_HOME/extensions/weather/, or run `zut ext install ./weather` | |
| 14 | + | // from this directory. After that, asking zut anything like | |
| 15 | 15 | // "what's the weather in Berlin?" makes the model call the tool. | |
| 16 | 16 | package main | |
| 17 | 17 | ||
| @@ -21,7 +21,7 @@ import ( | |||
| 21 | 21 | "fmt" | |
| 22 | 22 | "strings" | |
| 23 | 23 | ||
| 24 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 24 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 25 | 25 | ) | |
| 26 | 26 | ||
| 27 | 27 | const weatherSchema = `{ | |
M examples/rpc/go/main.go +4 -4
| @@ -1,6 +1,6 @@ | |||
| 1 | - | // Tiny Go program that drives `zot rpc` as a subprocess. | |
| 1 | + | // Tiny Go program that drives `zut rpc` as a subprocess. | |
| 2 | 2 | // | |
| 3 | - | // For an in-process Go embedding, use github.com/patriceckhart/zot/packages/agent/sdk | |
| 3 | + | // For an in-process Go embedding, use github.com/bnema/zut/packages/agent/sdk | |
| 4 | 4 | // instead — this example exists to show what consumers in OTHER | |
| 5 | 5 | // languages have to do. | |
| 6 | 6 | package main | |
| @@ -22,7 +22,7 @@ func main() { | |||
| 22 | 22 | } | |
| 23 | 23 | prompt := strings.Join(os.Args[1:], " ") | |
| 24 | 24 | ||
| 25 | - | cmd := exec.Command("zot", "rpc") | |
| 25 | + | cmd := exec.Command("zut", "rpc") | |
| 26 | 26 | cmd.Stderr = os.Stderr | |
| 27 | 27 | stdin, err := cmd.StdinPipe() | |
| 28 | 28 | must(err) | |
| @@ -30,7 +30,7 @@ func main() { | |||
| 30 | 30 | must(err) | |
| 31 | 31 | must(cmd.Start()) | |
| 32 | 32 | ||
| 33 | - | if tok := os.Getenv("ZOTCORE_RPC_TOKEN"); tok != "" { | |
| 33 | + | if tok := os.Getenv("ZUTCORE_RPC_TOKEN"); tok != "" { | |
| 34 | 34 | send(stdin, map[string]any{"id": "0", "type": "hello", "token": tok}) | |
| 35 | 35 | } | |
| 36 | 36 | send(stdin, map[string]any{ | |
R examples/rpc/node/zot-client.js → examples/rpc/node/zut-client.js +8 -8 renamed, 85% similar
| @@ -1,9 +1,9 @@ | |||
| 1 | - | // Minimal Node client for the `zot rpc` JSON protocol. | |
| 1 | + | // Minimal Node client for the `zut rpc` JSON protocol. | |
| 2 | 2 | // | |
| 3 | 3 | // Usage: | |
| 4 | - | // node zot-client.js "fix the failing test" | |
| 4 | + | // node zut-client.js "fix the failing test" | |
| 5 | 5 | // | |
| 6 | - | // Spawns `zot rpc`, sends one prompt, prints assistant text as it | |
| 6 | + | // Spawns `zut rpc`, sends one prompt, prints assistant text as it | |
| 7 | 7 | // streams, and exits when the turn finishes. Pure stdlib — no | |
| 8 | 8 | // dependencies. See docs/rpc.md for the full schema. | |
| 9 | 9 | "use strict"; | |
| @@ -12,9 +12,9 @@ const { spawn } = require("node:child_process"); | |||
| 12 | 12 | const readline = require("node:readline"); | |
| 13 | 13 | const { randomBytes } = require("node:crypto"); | |
| 14 | 14 | ||
| 15 | - | class ZotClient { | |
| 15 | + | class ZutClient { | |
| 16 | 16 | constructor(...flags) { | |
| 17 | - | this.proc = spawn("zot", ["rpc", ...flags], { | |
| 17 | + | this.proc = spawn("zut", ["rpc", ...flags], { | |
| 18 | 18 | stdio: ["pipe", "pipe", "inherit"], | |
| 19 | 19 | env: process.env, | |
| 20 | 20 | }); | |
| @@ -52,13 +52,13 @@ class ZotClient { | |||
| 52 | 52 | async function main() { | |
| 53 | 53 | const prompt = process.argv.slice(2).join(" "); | |
| 54 | 54 | if (!prompt) { | |
| 55 | - | console.error("usage: node zot-client.js <prompt>"); | |
| 55 | + | console.error("usage: node zut-client.js <prompt>"); | |
| 56 | 56 | process.exit(2); | |
| 57 | 57 | } | |
| 58 | 58 | ||
| 59 | - | const client = new ZotClient(); | |
| 59 | + | const client = new ZutClient(); | |
| 60 | 60 | ||
| 61 | - | const token = process.env.ZOTCORE_RPC_TOKEN; | |
| 61 | + | const token = process.env.ZUTCORE_RPC_TOKEN; | |
| 62 | 62 | if (token) client.send({ type: "hello", token }); | |
| 63 | 63 | ||
| 64 | 64 | client.send({ type: "prompt", message: prompt }); | |
D examples/rpc/python/__pycache__/zot_client.cpython-314.pyc +0 -0 binary file: no text diff
binary file: no text diff
R examples/rpc/python/zot_client.py → examples/rpc/python/zut_client.py +8 -8 renamed, 89% similar
| @@ -1,9 +1,9 @@ | |||
| 1 | - | """Minimal Python client for the `zot rpc` JSON protocol. | |
| 1 | + | """Minimal Python client for the `zut rpc` JSON protocol. | |
| 2 | 2 | ||
| 3 | 3 | Usage: | |
| 4 | - | python zot_client.py "fix the failing test" | |
| 4 | + | python zut_client.py "fix the failing test" | |
| 5 | 5 | ||
| 6 | - | Spawns `zot rpc`, sends one prompt, prints assistant text as it streams, | |
| 6 | + | Spawns `zut rpc`, sends one prompt, prints assistant text as it streams, | |
| 7 | 7 | and exits when the turn finishes. No external dependencies — stdlib | |
| 8 | 8 | only. Implements just enough of the protocol to be useful as a | |
| 9 | 9 | starting point; see docs/rpc.md for the full schema. | |
| @@ -18,9 +18,9 @@ import threading | |||
| 18 | 18 | import uuid | |
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | - | class ZotClient: | |
| 21 | + | class ZutClient: | |
| 22 | 22 | def __init__(self, *flags: str) -> None: | |
| 23 | - | argv = ["zot", "rpc", *flags] | |
| 23 | + | argv = ["zut", "rpc", *flags] | |
| 24 | 24 | env = os.environ.copy() | |
| 25 | 25 | self.proc = subprocess.Popen( | |
| 26 | 26 | argv, | |
| @@ -67,13 +67,13 @@ class ZotClient: | |||
| 67 | 67 | ||
| 68 | 68 | def main() -> int: | |
| 69 | 69 | if len(sys.argv) < 2: | |
| 70 | - | print("usage: zot_client.py <prompt>", file=sys.stderr) | |
| 70 | + | print("usage: zut_client.py <prompt>", file=sys.stderr) | |
| 71 | 71 | return 2 | |
| 72 | 72 | prompt = " ".join(sys.argv[1:]) | |
| 73 | 73 | ||
| 74 | - | client = ZotClient() | |
| 74 | + | client = ZutClient() | |
| 75 | 75 | try: | |
| 76 | - | token = os.environ.get("ZOTCORE_RPC_TOKEN") | |
| 76 | + | token = os.environ.get("ZUTCORE_RPC_TOKEN") | |
| 77 | 77 | if token: | |
| 78 | 78 | client.send(type="hello", token=token) | |
| 79 | 79 | ||
M examples/rpc/shell/prompt.sh +6 -6
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | #!/usr/bin/env bash | |
| 2 | - | # Minimal shell client for the `zot rpc` JSON protocol. | |
| 2 | + | # Minimal shell client for the `zut rpc` JSON protocol. | |
| 3 | 3 | # | |
| 4 | 4 | # Usage: | |
| 5 | 5 | # ./prompt.sh "fix the failing test" | |
| @@ -18,17 +18,17 @@ prompt="$*" | |||
| 18 | 18 | # Build the prompt command frame with jq so quotes are escaped properly. | |
| 19 | 19 | cmd=$(jq -nc --arg msg "$prompt" '{id:"1",type:"prompt",message:$msg}') | |
| 20 | 20 | ||
| 21 | - | # Pipe the command into zot rpc; pipe its stdout through jq to react | |
| 21 | + | # Pipe the command into zut rpc; pipe its stdout through jq to react | |
| 22 | 22 | # to events. The trailing `cat` keeps the input pipe open until the | |
| 23 | 23 | # subprocess exits on its own (after `done`). | |
| 24 | 24 | { | |
| 25 | - | if [ -n "${ZOTCORE_RPC_TOKEN:-}" ]; then | |
| 26 | - | jq -nc --arg t "$ZOTCORE_RPC_TOKEN" '{id:"0",type:"hello",token:$t}' | |
| 25 | + | if [ -n "${ZUTCORE_RPC_TOKEN:-}" ]; then | |
| 26 | + | jq -nc --arg t "$ZUTCORE_RPC_TOKEN" '{id:"0",type:"hello",token:$t}' | |
| 27 | 27 | fi | |
| 28 | 28 | echo "$cmd" | |
| 29 | - | # Block here so stdin stays open until zot exits. | |
| 29 | + | # Block here so stdin stays open until zut exits. | |
| 30 | 30 | cat | |
| 31 | - | } | zot rpc | while IFS= read -r line; do | |
| 31 | + | } | zut rpc | while IFS= read -r line; do | |
| 32 | 32 | type=$(echo "$line" | jq -r '.type // empty') | |
| 33 | 33 | case "$type" in | |
| 34 | 34 | text_delta) | |
M examples/sdk/main.go +3 -3
| @@ -1,5 +1,5 @@ | |||
| 1 | - | // In-process Go embedding of the zot agent runtime via the sdk package. | |
| 2 | - | // Compare to examples/rpc/go which spawns `zot rpc` as a subprocess. | |
| 1 | + | // In-process Go embedding of the zut agent runtime via the sdk package. | |
| 2 | + | // Compare to examples/rpc/go which spawns `zut rpc` as a subprocess. | |
| 3 | 3 | package main | |
| 4 | 4 | ||
| 5 | 5 | import ( | |
| @@ -8,7 +8,7 @@ import ( | |||
| 8 | 8 | "os" | |
| 9 | 9 | "strings" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/sdk" | |
| 11 | + | "github.com/bnema/zut/packages/agent/sdk" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | func main() { | |
M examples/themes/custom-spinner.json +1 -1
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | { | |
| 2 | 2 | "name": "custom-spinner", | |
| 3 | - | "description": "An alternative spinner for zot that only displays a single spinner text.", | |
| 3 | + | "description": "An alternative spinner for zut that only displays a single spinner text.", | |
| 4 | 4 | "colors": { | |
| 5 | 5 | "dark": { | |
| 6 | 6 | "spinner_frames": [ | |
M examples/themes/vercel.json +2 -2
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | { | |
| 2 | 2 | "name": "vercel", | |
| 3 | - | "description": "Vercel-inspired zot theme based on gantoreno/vscode-vercel. Uses the same core palette as the VS Code theme: pink keywords, blue constants/links, purple functions, green strings/success, amber warnings, and neutral Vercel grays for chrome.", | |
| 3 | + | "description": "Vercel-inspired zut theme based on gantoreno/vscode-vercel. Uses the same core palette as the VS Code theme: pink keywords, blue constants/links, purple functions, green strings/success, amber warnings, and neutral Vercel grays for chrome.", | |
| 4 | 4 | "color_descriptions": { | |
| 5 | 5 | "fg": "Default foreground text, based on Vercel editor.foreground / terminal.foreground.", | |
| 6 | 6 | "muted": "Secondary text, dividers, gutters, footers, inactive hints, based on Vercel foreground/sidebar/status gray.", | |
| @@ -8,7 +8,7 @@ | |||
| 8 | 8 | "user": "User role label color; kept for compatibility with older UI surfaces.", | |
| 9 | 9 | "user_bubble_bg": "Background behind user message rows, based on Vercel editor.selectionBackground / lineHighlightBackground.", | |
| 10 | 10 | "user_bubble_fg": "Text foreground inside user message rows.", | |
| 11 | - | "assistant": "Assistant/zot accent color and welcome label, mapped to Vercel blue.", | |
| 11 | + | "assistant": "Assistant/zut accent color and welcome label, mapped to Vercel blue.", | |
| 12 | 12 | "tool": "Tool names, successful marks, additions in diffs, based on Vercel green.", | |
| 13 | 13 | "tool_out": "Plain tool output text.", | |
| 14 | 14 | "error": "Errors, refused tool calls, deletions in diffs, based on Vercel error/list error colors.", | |
M go.mod +1 -1
| @@ -1,4 +1,4 @@ | |||
| 1 | - | module github.com/patriceckhart/zot | |
| 1 | + | module github.com/bnema/zut | |
| 2 | 2 | ||
| 3 | 3 | go 1.26 | |
| 4 | 4 | ||
M install.ps1 +15 -15
| @@ -1,16 +1,16 @@ | |||
| 1 | - | # zot installer for Windows (PowerShell). | |
| 1 | + | # zut installer for Windows (PowerShell). | |
| 2 | 2 | # | |
| 3 | 3 | # Usage (in PowerShell): | |
| 4 | - | # iwr -useb https://raw.githubusercontent.com/patriceckhart/zot/main/install.ps1 | iex | |
| 4 | + | # iwr -useb https://raw.githubusercontent.com/bnema/zut/main/install.ps1 | iex | |
| 5 | 5 | # | |
| 6 | 6 | # Or with arguments: | |
| 7 | - | # $env:ZOT_VERSION = "v0.0.1" | |
| 8 | - | # $env:ZOT_PREFIX = "$HOME\bin" | |
| 9 | - | # iwr -useb https://raw.githubusercontent.com/patriceckhart/zot/main/install.ps1 | iex | |
| 7 | + | # $env:ZUT_VERSION = "v0.1.0" | |
| 8 | + | # $env:ZUT_PREFIX = "$HOME\bin" | |
| 9 | + | # iwr -useb https://raw.githubusercontent.com/bnema/zut/main/install.ps1 | iex | |
| 10 | 10 | # | |
| 11 | 11 | # Detects architecture, downloads the matching .zip from the GitHub | |
| 12 | - | # release, verifies the sha256 against checksums.txt, extracts zot.exe, | |
| 13 | - | # and moves it into $ZOT_PREFIX (defaults to $HOME\bin, added to PATH | |
| 12 | + | # release, verifies the sha256 against checksums.txt, extracts zut.exe, | |
| 13 | + | # and moves it into $ZUT_PREFIX (defaults to $HOME\bin, added to PATH | |
| 14 | 14 | # via the User environment if missing). | |
| 15 | 15 | # | |
| 16 | 16 | # $env:GITHUB_TOKEN is optional for the public repo. Set it to a PAT | |
| @@ -21,15 +21,15 @@ | |||
| 21 | 21 | ||
| 22 | 22 | [CmdletBinding()] | |
| 23 | 23 | param( | |
| 24 | - | [string]$Version = $env:ZOT_VERSION, | |
| 25 | - | [string]$Prefix = $env:ZOT_PREFIX | |
| 24 | + | [string]$Version = $env:ZUT_VERSION, | |
| 25 | + | [string]$Prefix = $env:ZUT_PREFIX | |
| 26 | 26 | ) | |
| 27 | 27 | ||
| 28 | 28 | $ErrorActionPreference = "Stop" | |
| 29 | 29 | ||
| 30 | - | $owner = "patriceckhart" | |
| 31 | - | $repo = "zot" | |
| 32 | - | $binary = "zot" | |
| 30 | + | $owner = "bnema" | |
| 31 | + | $repo = "zut" | |
| 32 | + | $binary = "zut" | |
| 33 | 33 | ||
| 34 | 34 | # Build Authorization header list once; used on every HTTP call so the | |
| 35 | 35 | # script works against private repos when $env:GITHUB_TOKEN is set. | |
| @@ -73,7 +73,7 @@ if ($Version -eq "latest") { | |||
| 73 | 73 | # GitHub's API wants a User-Agent; Invoke-RestMethod sets one, but be | |
| 74 | 74 | # explicit so corporate proxies that strip it don't trip a 403. | |
| 75 | 75 | $apiHeaders = @{} + $headers | |
| 76 | - | if (-not $apiHeaders.ContainsKey("User-Agent")) { $apiHeaders["User-Agent"] = "zot-installer" } | |
| 76 | + | if (-not $apiHeaders.ContainsKey("User-Agent")) { $apiHeaders["User-Agent"] = "zut-installer" } | |
| 77 | 77 | $apiHeaders["Accept"] = "application/vnd.github+json" | |
| 78 | 78 | ||
| 79 | 79 | try { | |
| @@ -106,7 +106,7 @@ $baseUrl = "https://github.com/$owner/$repo/releases/download/$Version" | |||
| 106 | 106 | $archiveUrl = "$baseUrl/$archive" | |
| 107 | 107 | $checksumUrl = "$baseUrl/checksums.txt" | |
| 108 | 108 | ||
| 109 | - | $tmp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("zot-install-" + [System.Guid]::NewGuid().ToString("N").Substring(0,8))) | |
| 109 | + | $tmp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("zut-install-" + [System.Guid]::NewGuid().ToString("N").Substring(0,8))) | |
| 110 | 110 | ||
| 111 | 111 | try { | |
| 112 | 112 | Msg "downloading $archive" | |
| @@ -152,7 +152,7 @@ try { | |||
| 152 | 152 | Warn " `$env:Path = `"$Prefix;`$env:Path`"" | |
| 153 | 153 | } | |
| 154 | 154 | ||
| 155 | - | Msg "installed. run: zot --help" | |
| 155 | + | Msg "installed. run: zut --help" | |
| 156 | 156 | } | |
| 157 | 157 | finally { | |
| 158 | 158 | Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue | |
M install.sh +15 -15
| @@ -1,24 +1,24 @@ | |||
| 1 | 1 | #!/usr/bin/env bash | |
| 2 | 2 | # | |
| 3 | - | # zot installer. | |
| 3 | + | # zut installer. | |
| 4 | 4 | # | |
| 5 | 5 | # Usage: | |
| 6 | - | # curl -fsSL https://raw.githubusercontent.com/patriceckhart/zot/main/install.sh | bash | |
| 7 | - | # curl -fsSL https://raw.githubusercontent.com/patriceckhart/zot/main/install.sh | bash -s -- v0.0.1 ~/bin | |
| 6 | + | # curl -fsSL https://raw.githubusercontent.com/bnema/zut/main/install.sh | bash | |
| 7 | + | # curl -fsSL https://raw.githubusercontent.com/bnema/zut/main/install.sh | bash -s -- v0.1.0 ~/bin | |
| 8 | 8 | # | |
| 9 | 9 | # Positional arguments: | |
| 10 | - | # $1 version — release tag (e.g. v0.0.1). Defaults to "latest". | |
| 10 | + | # $1 version — release tag (e.g. v0.1.0). Defaults to "latest". | |
| 11 | 11 | # $2 prefix — install directory. Defaults to the first writable | |
| 12 | 12 | # directory in: /usr/local/bin, $HOME/.local/bin, | |
| 13 | 13 | # $HOME/bin. Created if missing. Add it to your PATH | |
| 14 | 14 | # if it isn't already. | |
| 15 | 15 | # | |
| 16 | 16 | # Environment overrides: | |
| 17 | - | # ZOT_VERSION same as $1 | |
| 18 | - | # ZOT_PREFIX same as $2 | |
| 17 | + | # ZUT_VERSION same as $1 | |
| 18 | + | # ZUT_PREFIX same as $2 | |
| 19 | 19 | # GITHUB_TOKEN personal access token — required while the repo is | |
| 20 | 20 | # private, ignored once it goes public. Must have at | |
| 21 | - | # least `contents:read` scope on the zot repository. | |
| 21 | + | # least `contents:read` scope on the zut repository. | |
| 22 | 22 | # | |
| 23 | 23 | # The script detects your OS and architecture, downloads the matching | |
| 24 | 24 | # archive from the GitHub release, verifies the sha256 against the | |
| @@ -28,12 +28,12 @@ | |||
| 28 | 28 | ||
| 29 | 29 | set -euo pipefail | |
| 30 | 30 | ||
| 31 | - | OWNER="patriceckhart" | |
| 32 | - | REPO="zot" | |
| 33 | - | BINARY="zot" | |
| 31 | + | OWNER="bnema" | |
| 32 | + | REPO="zut" | |
| 33 | + | BINARY="zut" | |
| 34 | 34 | ||
| 35 | - | VERSION="${1:-${ZOT_VERSION:-latest}}" | |
| 36 | - | PREFIX="${2:-${ZOT_PREFIX:-}}" | |
| 35 | + | VERSION="${1:-${ZUT_VERSION:-latest}}" | |
| 36 | + | PREFIX="${2:-${ZUT_PREFIX:-}}" | |
| 37 | 37 | ||
| 38 | 38 | msg() { printf "\033[1m==>\033[0m %s\n" "$*"; } | |
| 39 | 39 | warn() { printf "\033[33mwarn:\033[0m %s\n" "$*" >&2; } | |
| @@ -175,6 +175,6 @@ case ":$PATH:" in | |||
| 175 | 175 | ;; | |
| 176 | 176 | esac | |
| 177 | 177 | ||
| 178 | - | msg "installed $("$PREFIX/$BINARY" --version 2>/dev/null || echo zot)" | |
| 179 | - | msg "run: zot (interactive tui)" | |
| 180 | - | msg "run: zot --help (all flags and subcommands)" | |
| 178 | + | msg "installed $("$PREFIX/$BINARY" --version 2>/dev/null || echo zut)" | |
| 179 | + | msg "run: zut (interactive tui)" | |
| 180 | + | msg "run: zut --help (all flags and subcommands)" | |
M packages/agent/args.go +34 -34
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "strconv" | |
| 7 | 7 | "strings" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | "golang.org/x/term" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| @@ -81,12 +81,12 @@ type Args struct { | |||
| 81 | 81 | // NoSkill disables ALL skill discovery for this run, including | |
| 82 | 82 | // the built-in skills compiled into the binary. The system | |
| 83 | 83 | // prompt loses its "Available skills" manifest and the `skill` | |
| 84 | - | // tool isn't registered. Useful for running zot without any | |
| 84 | + | // tool isn't registered. Useful for running zut without any | |
| 85 | 85 | // extra context biasing the model. | |
| 86 | 86 | NoSkill bool | |
| 87 | 87 | ||
| 88 | 88 | // WithSkills controls loading user-installed skills from | |
| 89 | - | // $ZOT_HOME/skills/, .zot/skills/, .claude/skills/, and | |
| 89 | + | // $ZUT_HOME/skills/, .zut/skills/, .claude/skills/, and | |
| 90 | 90 | // .agents/skills/. It defaults to true; --no-skill disables all | |
| 91 | 91 | // skill discovery, including built-ins. | |
| 92 | 92 | WithSkills bool | |
| @@ -107,8 +107,8 @@ type Args struct { | |||
| 107 | 107 | // freely so automated workflows keep working. | |
| 108 | 108 | NoYolo bool | |
| 109 | 109 | ||
| 110 | - | // Yes accepts zotfile launch consent without an interactive | |
| 111 | - | // Allow? prompt (zot run -y / --yes). Durable consent receipts | |
| 110 | + | // Yes accepts zutfile launch consent without an interactive | |
| 111 | + | // Allow? prompt (zut run -y / --yes). Durable consent receipts | |
| 112 | 112 | // are still written for modes other than bash ask. | |
| 113 | 113 | Yes bool | |
| 114 | 114 | ||
| @@ -119,7 +119,7 @@ type Args struct { | |||
| 119 | 119 | ||
| 120 | 120 | Prompt string // concatenated positional args | |
| 121 | 121 | ||
| 122 | - | // StartupPre is an optional zotfile entry.pre value. Interactive | |
| 122 | + | // StartupPre is an optional zutfile entry.pre value. Interactive | |
| 123 | 123 | // mode auto-submits it once at startup before InitialInput handling. | |
| 124 | 124 | StartupPre string | |
| 125 | 125 | ||
| @@ -137,8 +137,8 @@ type Args struct { | |||
| 137 | 137 | // locally. | |
| 138 | 138 | Subagent string | |
| 139 | 139 | ||
| 140 | - | // AgentName/AgentDataDir/PermissionSet are populated by `zot run` | |
| 141 | - | // for local zotfile agents. They scope sessions and enforce the | |
| 140 | + | // AgentName/AgentDataDir/PermissionSet are populated by `zut run` | |
| 141 | + | // for local zutfile agents. They scope sessions and enforce the | |
| 142 | 142 | // manifest's declared file/bash permissions. | |
| 143 | 143 | AgentName string | |
| 144 | 144 | AgentDataDir string | |
| @@ -364,7 +364,7 @@ func ParseArgs(in []string) (Args, error) { | |||
| 364 | 364 | } | |
| 365 | 365 | ||
| 366 | 366 | // PrintHelp writes the help text to stderr. When stderr is a TTY it | |
| 367 | - | // uses the same palette as zot's TUI; when redirected it falls back to | |
| 367 | + | // uses the same palette as zut's TUI; when redirected it falls back to | |
| 368 | 368 | // plain text with no ANSI escapes. | |
| 369 | 369 | func PrintHelp(version string) { | |
| 370 | 370 | th := tui.Dark | |
| @@ -427,42 +427,42 @@ func PrintHelp(version string) { | |||
| 427 | 427 | fmt.Fprintln(os.Stderr) | |
| 428 | 428 | var headline string | |
| 429 | 429 | if useColor { | |
| 430 | - | headline = th.AccentBar(th.Assistant) + assistant(tui.Bold("zot. yet another coding agent harness.")) | |
| 430 | + | headline = th.AccentBar(th.Assistant) + assistant(tui.Bold("zut. yet another coding agent harness.")) | |
| 431 | 431 | } else { | |
| 432 | - | headline = "zot. yet another coding agent harness." | |
| 432 | + | headline = "zut. yet another coding agent harness." | |
| 433 | 433 | } | |
| 434 | 434 | fmt.Fprintln(os.Stderr, headline) | |
| 435 | 435 | fmt.Fprintln(os.Stderr, muted("ask anything, or type /help inside the tui to see commands.")) | |
| 436 | 436 | fmt.Fprintf(os.Stderr, "%s %s\n", muted("version:"), fg(version)) | |
| 437 | 437 | ||
| 438 | 438 | section("modes", | |
| 439 | - | row{"zot", "interactive tui"}, | |
| 440 | - | row{"zot \"prompt\"", "interactive, pre-filled prompt"}, | |
| 441 | - | row{"zot -p \"prompt\"", "print final text, exit"}, | |
| 442 | - | row{"zot --stream \"prompt\"", "stream assistant text live, exit"}, | |
| 443 | - | row{"zot --json \"prompt\"", "newline-delimited json events, exit"}, | |
| 444 | - | row{"zot rpc", "json-rpc loop on stdin/stdout (see docs/rpc.md)"}, | |
| 439 | + | row{"zut", "interactive tui"}, | |
| 440 | + | row{"zut \"prompt\"", "interactive, pre-filled prompt"}, | |
| 441 | + | row{"zut -p \"prompt\"", "print final text, exit"}, | |
| 442 | + | row{"zut --stream \"prompt\"", "stream assistant text live, exit"}, | |
| 443 | + | row{"zut --json \"prompt\"", "newline-delimited json events, exit"}, | |
| 444 | + | row{"zut rpc", "json-rpc loop on stdin/stdout (see docs/rpc.md)"}, | |
| 445 | 445 | ) | |
| 446 | 446 | section("extensions", | |
| 447 | - | row{"zot ext list", "list installed extensions"}, | |
| 448 | - | row{"zot ext install <path|git-url>", "install from a local path or Git URL"}, | |
| 449 | - | row{"zot ext install --build=go <path>", "build local Go source, then install"}, | |
| 450 | - | row{"zot --ext ./path/to/ext", "load an extension for this run only"}, | |
| 451 | - | row{"zot ext help", "show all extension subcommands"}, | |
| 447 | + | row{"zut ext list", "list installed extensions"}, | |
| 448 | + | row{"zut ext install <path|git-url>", "install from a local path or Git URL"}, | |
| 449 | + | row{"zut ext install --build=go <path>", "build local Go source, then install"}, | |
| 450 | + | row{"zut --ext ./path/to/ext", "load an extension for this run only"}, | |
| 451 | + | row{"zut ext help", "show all extension subcommands"}, | |
| 452 | 452 | ) | |
| 453 | 453 | section("self-update", | |
| 454 | - | row{"zot update", "download and install the latest release"}, | |
| 455 | - | row{"zot update --check", "show whether a new release is available"}, | |
| 454 | + | row{"zut update", "download and install the latest release"}, | |
| 455 | + | row{"zut update --check", "show whether a new release is available"}, | |
| 456 | 456 | ) | |
| 457 | 457 | section("telegram", | |
| 458 | - | row{"zot telegram-bot setup", "configure a telegram bot (from BotFather)"}, | |
| 459 | - | row{"zot telegram-bot run", "foreground bridge (ctrl+c to stop)"}, | |
| 460 | - | row{"zot telegram-bot start", "background bridge (detached)"}, | |
| 461 | - | row{"zot telegram-bot stop", "stop the background bridge"}, | |
| 462 | - | row{"zot telegram-bot logs [-f]", "tail the background bridge log"}, | |
| 463 | - | row{"zot telegram-bot status", "config + running state"}, | |
| 464 | - | row{"zot telegram-bot reset", "forget saved token"}, | |
| 465 | - | row{"zot tg ...", "short alias for telegram-bot"}, | |
| 458 | + | row{"zut telegram-bot setup", "configure a telegram bot (from BotFather)"}, | |
| 459 | + | row{"zut telegram-bot run", "foreground bridge (ctrl+c to stop)"}, | |
| 460 | + | row{"zut telegram-bot start", "background bridge (detached)"}, | |
| 461 | + | row{"zut telegram-bot stop", "stop the background bridge"}, | |
| 462 | + | row{"zut telegram-bot logs [-f]", "tail the background bridge log"}, | |
| 463 | + | row{"zut telegram-bot status", "config + running state"}, | |
| 464 | + | row{"zut telegram-bot reset", "forget saved token"}, | |
| 465 | + | row{"zut tg ...", "short alias for telegram-bot"}, | |
| 466 | 466 | ) | |
| 467 | 467 | section("provider and model flags", | |
| 468 | 468 | row{"--provider", "provider to use (anthropic|openai|openai-codex|kimi|deepseek|google|ollama|llama.cpp)"}, | |
| @@ -488,7 +488,7 @@ func PrintHelp(version string) { | |||
| 488 | 488 | row{"--no-lsp", "disable the built-in LSP/linter tool"}, | |
| 489 | 489 | row{"--tools csv", "only enable the listed tools (include lsp explicitly)"}, | |
| 490 | 490 | row{"--no-yolo", "ask before running every tool call"}, | |
| 491 | - | row{"-y, --yes", "accept zot run consent without prompting"}, | |
| 491 | + | row{"-y, --yes", "accept zut run consent without prompting"}, | |
| 492 | 492 | row{"--no-ext", "skip extension discovery for this run"}, | |
| 493 | 493 | row{"--no-skill", "skip all skill discovery for this run"}, | |
| 494 | 494 | ) | |
M packages/agent/botcmd.go +63 -63
| @@ -14,10 +14,10 @@ import ( | |||
| 14 | 14 | "syscall" | |
| 15 | 15 | "time" | |
| 16 | 16 | ||
| 17 | - | "github.com/patriceckhart/zot/packages/agent/modes/bot" | |
| 18 | - | "github.com/patriceckhart/zot/packages/agent/modes/telegram" | |
| 19 | - | "github.com/patriceckhart/zot/packages/core" | |
| 20 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 17 | + | "github.com/bnema/zut/packages/agent/modes/bot" | |
| 18 | + | "github.com/bnema/zut/packages/agent/modes/telegram" | |
| 19 | + | "github.com/bnema/zut/packages/core" | |
| 20 | + | "github.com/bnema/zut/packages/provider" | |
| 21 | 21 | "golang.org/x/term" | |
| 22 | 22 | ) | |
| 23 | 23 | ||
| @@ -28,7 +28,7 @@ import ( | |||
| 28 | 28 | // inherited). See botcmd_unix.go and botcmd_windows.go. | |
| 29 | 29 | var detachChild func(cmd *exec.Cmd) | |
| 30 | 30 | ||
| 31 | - | // runBotCommand dispatches `zot <protocol>-bot ...` subcommands via | |
| 31 | + | // runBotCommand dispatches `zut <protocol>-bot ...` subcommands via | |
| 32 | 32 | // the botSpec registry. Returns true if rawArgs begins with a | |
| 33 | 33 | // recognised subcommand, false otherwise. The short alias "tg" (and | |
| 34 | 34 | // "mx" for matrix) are also accepted. | |
| @@ -70,29 +70,29 @@ func runBotCommand(rawArgs []string, version string) (handled bool, err error) { | |||
| 70 | 70 | } | |
| 71 | 71 | } | |
| 72 | 72 | ||
| 73 | - | // botStart launches `zot <spec.subcommand> run` as a detached child | |
| 73 | + | // botStart launches `zut <spec.subcommand> run` as a detached child | |
| 74 | 74 | // process, writes its pid to the spec's pid file, and returns | |
| 75 | 75 | // immediately. Stdout/stderr of the child are redirected to the spec's | |
| 76 | 76 | // log file. | |
| 77 | 77 | func botStart(spec *botSpec, rawTail []string) error { | |
| 78 | 78 | // Refuse to start if another bot is already running. | |
| 79 | - | if pid, alive, _ := bot.IsRunningAt(spec.pidPath(ZotHome())); alive { | |
| 80 | - | return fmt.Errorf("%s is already running (pid %d); use `zot %s stop` first", spec.name, pid, spec.subcommand) | |
| 79 | + | if pid, alive, _ := bot.IsRunningAt(spec.pidPath(ZutHome())); alive { | |
| 80 | + | return fmt.Errorf("%s is already running (pid %d); use `zut %s stop` first", spec.name, pid, spec.subcommand) | |
| 81 | 81 | } | |
| 82 | - | _ = bot.RemovePIDFile(spec.pidPath(ZotHome())) // clear any stale pid file | |
| 82 | + | _ = bot.RemovePIDFile(spec.pidPath(ZutHome())) // clear any stale pid file | |
| 83 | 83 | ||
| 84 | - | if ok, cerr := spec.configured(ZotHome()); cerr != nil { | |
| 84 | + | if ok, cerr := spec.configured(ZutHome()); cerr != nil { | |
| 85 | 85 | return cerr | |
| 86 | 86 | } else if !ok { | |
| 87 | - | return fmt.Errorf("%s is not configured — run `zot %s setup` first", spec.name, spec.subcommand) | |
| 87 | + | return fmt.Errorf("%s is not configured — run `zut %s setup` first", spec.name, spec.subcommand) | |
| 88 | 88 | } | |
| 89 | 89 | ||
| 90 | 90 | self, err := os.Executable() | |
| 91 | 91 | if err != nil { | |
| 92 | - | return fmt.Errorf("locate zot binary: %w", err) | |
| 92 | + | return fmt.Errorf("locate zut binary: %w", err) | |
| 93 | 93 | } | |
| 94 | 94 | ||
| 95 | - | logPath := spec.logPath(ZotHome()) | |
| 95 | + | logPath := spec.logPath(ZutHome()) | |
| 96 | 96 | if err := os.MkdirAll(filepath.Dir(logPath), 0o755); err != nil { | |
| 97 | 97 | return err | |
| 98 | 98 | } | |
| @@ -107,11 +107,11 @@ func botStart(spec *botSpec, rawTail []string) error { | |||
| 107 | 107 | // Users hit cryptic tls / exec errors on that path; fail clearly. | |
| 108 | 108 | if strings.Contains(self, string(os.PathSeparator)+"go-build") || | |
| 109 | 109 | strings.Contains(self, string(os.PathSeparator)+"go-tmp") { | |
| 110 | - | return fmt.Errorf("detected `go run` temp binary at %s — run `make install` (or copy ./bin/zot to your PATH) and use the installed binary for `start`", self) | |
| 110 | + | return fmt.Errorf("detected `go run` temp binary at %s — run `make install` (or copy ./bin/zut to your PATH) and use the installed binary for `start`", self) | |
| 111 | 111 | } | |
| 112 | 112 | ||
| 113 | - | // Child argv: same flags the user passed to `zot <sub> start`, | |
| 114 | - | // mapped to `zot <sub> run`. Preserves --provider, --model, --cwd, etc. | |
| 113 | + | // Child argv: same flags the user passed to `zut <sub> start`, | |
| 114 | + | // mapped to `zut <sub> run`. Preserves --provider, --model, --cwd, etc. | |
| 115 | 115 | args := append([]string{spec.subcommand, "run"}, rawTail...) | |
| 116 | 116 | cmd := exec.Command(self, args...) | |
| 117 | 117 | cmd.Stdout = logFile | |
| @@ -125,28 +125,28 @@ func botStart(spec *botSpec, rawTail []string) error { | |||
| 125 | 125 | if err := cmd.Start(); err != nil { | |
| 126 | 126 | return fmt.Errorf("spawn: %w", err) | |
| 127 | 127 | } | |
| 128 | - | if err := bot.WritePIDFile(spec.pidPath(ZotHome()), cmd.Process.Pid); err != nil { | |
| 128 | + | if err := bot.WritePIDFile(spec.pidPath(ZutHome()), cmd.Process.Pid); err != nil { | |
| 129 | 129 | _ = cmd.Process.Kill() | |
| 130 | 130 | return fmt.Errorf("write pid: %w", err) | |
| 131 | 131 | } | |
| 132 | 132 | // Don't wait() — detach. OS will reparent the child to init when we exit. | |
| 133 | 133 | go func() { _ = cmd.Process.Release() }() | |
| 134 | 134 | ||
| 135 | - | fmt.Printf("started zot %s as pid %d (logs: %s)\n", spec.subcommand, cmd.Process.Pid, logPath) | |
| 136 | - | fmt.Printf("use `zot %s logs -f` to tail, `zot %s stop` to stop.\n", spec.subcommand, spec.subcommand) | |
| 135 | + | fmt.Printf("started zut %s as pid %d (logs: %s)\n", spec.subcommand, cmd.Process.Pid, logPath) | |
| 136 | + | fmt.Printf("use `zut %s logs -f` to tail, `zut %s stop` to stop.\n", spec.subcommand, spec.subcommand) | |
| 137 | 137 | return nil | |
| 138 | 138 | } | |
| 139 | 139 | ||
| 140 | 140 | // botStop sends SIGTERM to the running bot (SIGKILL if it doesn't | |
| 141 | 141 | // exit within 5s) and cleans up the pid file. | |
| 142 | 142 | func botStop(spec *botSpec) error { | |
| 143 | - | pid, alive, err := bot.IsRunningAt(spec.pidPath(ZotHome())) | |
| 143 | + | pid, alive, err := bot.IsRunningAt(spec.pidPath(ZutHome())) | |
| 144 | 144 | if err != nil { | |
| 145 | 145 | return err | |
| 146 | 146 | } | |
| 147 | 147 | if !alive { | |
| 148 | 148 | if pid > 0 { | |
| 149 | - | _ = bot.RemovePIDFile(spec.pidPath(ZotHome())) | |
| 149 | + | _ = bot.RemovePIDFile(spec.pidPath(ZutHome())) | |
| 150 | 150 | fmt.Printf("no live process; cleared stale pid %d\n", pid) | |
| 151 | 151 | return nil | |
| 152 | 152 | } | |
| @@ -156,7 +156,7 @@ func botStop(spec *botSpec) error { | |||
| 156 | 156 | if err := bot.StopProcess(pid, 5*time.Second); err != nil { | |
| 157 | 157 | return fmt.Errorf("stop pid %d: %w", pid, err) | |
| 158 | 158 | } | |
| 159 | - | _ = bot.RemovePIDFile(spec.pidPath(ZotHome())) | |
| 159 | + | _ = bot.RemovePIDFile(spec.pidPath(ZutHome())) | |
| 160 | 160 | fmt.Printf("stopped pid %d\n", pid) | |
| 161 | 161 | return nil | |
| 162 | 162 | } | |
| @@ -169,7 +169,7 @@ func botLogs(spec *botSpec, rawTail []string) error { | |||
| 169 | 169 | follow = true | |
| 170 | 170 | } | |
| 171 | 171 | } | |
| 172 | - | p := spec.logPath(ZotHome()) | |
| 172 | + | p := spec.logPath(ZutHome()) | |
| 173 | 173 | f, err := os.Open(p) | |
| 174 | 174 | if errors.Is(err, os.ErrNotExist) { | |
| 175 | 175 | fmt.Println("no log file at", p) | |
| @@ -220,12 +220,12 @@ func botRun(spec *botSpec, rawTail []string, version string) error { | |||
| 220 | 220 | } | |
| 221 | 221 | defer closeResolvedLSP(resolved) | |
| 222 | 222 | ||
| 223 | - | if ok, cerr := spec.configured(ZotHome()); cerr != nil { | |
| 223 | + | if ok, cerr := spec.configured(ZutHome()); cerr != nil { | |
| 224 | 224 | return cerr | |
| 225 | 225 | } else if !ok { | |
| 226 | - | return fmt.Errorf("%s is not configured — run `zot %s setup` first", spec.name, spec.subcommand) | |
| 226 | + | return fmt.Errorf("%s is not configured — run `zut %s setup` first", spec.name, spec.subcommand) | |
| 227 | 227 | } | |
| 228 | - | adapter, err := spec.newAdapter(ZotHome()) | |
| 228 | + | adapter, err := spec.newAdapter(ZutHome()) | |
| 229 | 229 | if err != nil { | |
| 230 | 230 | return err | |
| 231 | 231 | } | |
| @@ -256,7 +256,7 @@ func botRun(spec *botSpec, rawTail []string, version string) error { | |||
| 256 | 256 | ||
| 257 | 257 | var runner *bot.Runner | |
| 258 | 258 | runner = bot.NewRunner(adapter, agent, bot.Config{ | |
| 259 | - | ZotHome: ZotHome(), | |
| 259 | + | ZutHome: ZutHome(), | |
| 260 | 260 | Provider: resolved.Provider, | |
| 261 | 261 | Model: resolved.Model, | |
| 262 | 262 | AuthMethod: resolved.AuthMethod, | |
| @@ -278,10 +278,10 @@ func botRun(spec *botSpec, rawTail []string, version string) error { | |||
| 278 | 278 | }, | |
| 279 | 279 | }) | |
| 280 | 280 | ||
| 281 | - | // Record our pid so `zot <sub> status` / `zot <sub> stop` can find us, | |
| 281 | + | // Record our pid so `zut <sub> status` / `zut <sub> stop` can find us, | |
| 282 | 282 | // regardless of whether we were started directly or via `start`. | |
| 283 | - | _ = bot.WritePIDFile(spec.pidPath(ZotHome()), os.Getpid()) | |
| 284 | - | defer bot.RemovePIDFile(spec.pidPath(ZotHome())) | |
| 283 | + | _ = bot.WritePIDFile(spec.pidPath(ZutHome()), os.Getpid()) | |
| 284 | + | defer bot.RemovePIDFile(spec.pidPath(ZutHome())) | |
| 285 | 285 | ||
| 286 | 286 | // Translate sigterm/sigint into a context cancel so the bot's goroutines | |
| 287 | 287 | // and the currently-running turn wind down cleanly. | |
| @@ -300,7 +300,7 @@ func botRun(spec *botSpec, rawTail []string, version string) error { | |||
| 300 | 300 | // but never prompts (no TTY picker); falls back to latest or new. | |
| 301 | 301 | func openOrCreateSessionForBot(args Args, r Resolved, ag *core.Agent, version string) (*core.Session, []any, error) { | |
| 302 | 302 | if args.Continue { | |
| 303 | - | if latest := core.LatestSession(ZotHome(), args.CWD); latest != "" { | |
| 303 | + | if latest := core.LatestSession(ZutHome(), args.CWD); latest != "" { | |
| 304 | 304 | s, msgs, err := core.OpenSession(latest) | |
| 305 | 305 | if err != nil { | |
| 306 | 306 | return nil, nil, err | |
| @@ -309,11 +309,11 @@ func openOrCreateSessionForBot(args Args, r Resolved, ag *core.Agent, version st | |||
| 309 | 309 | return s, nil, nil | |
| 310 | 310 | } | |
| 311 | 311 | } | |
| 312 | - | s, err := core.NewSession(ZotHome(), args.CWD, r.Provider, r.Model, version) | |
| 312 | + | s, err := core.NewSession(ZutHome(), args.CWD, r.Provider, r.Model, version) | |
| 313 | 313 | return s, nil, err | |
| 314 | 314 | } | |
| 315 | 315 | ||
| 316 | - | // maskToken returns "123456:ABC...xyz" so copies of zot telegram-bot status can be | |
| 316 | + | // maskToken returns "123456:ABC...xyz" so copies of zut telegram-bot status can be | |
| 317 | 317 | // pasted into bug reports without leaking the full token. | |
| 318 | 318 | func maskToken(tok string) string { | |
| 319 | 319 | if len(tok) <= 10 { | |
| @@ -341,8 +341,8 @@ func telegramSpec() *botSpec { | |||
| 341 | 341 | aliases: []string{"tg"}, | |
| 342 | 342 | pidPath: telegram.PIDPath, | |
| 343 | 343 | logPath: telegram.LogPath, | |
| 344 | - | configured: func(zotHome string) (bool, error) { | |
| 345 | - | cfg, err := telegram.LoadConfig(zotHome) | |
| 344 | + | configured: func(zutHome string) (bool, error) { | |
| 345 | + | cfg, err := telegram.LoadConfig(zutHome) | |
| 346 | 346 | if err != nil { | |
| 347 | 347 | return false, err | |
| 348 | 348 | } | |
| @@ -352,37 +352,37 @@ func telegramSpec() *botSpec { | |||
| 352 | 352 | setup: telegramBotSetup, | |
| 353 | 353 | status: telegramBotStatus, | |
| 354 | 354 | reset: telegramBotReset, | |
| 355 | - | newAdapter: func(zotHome string) (bot.BotAdapter, error) { | |
| 356 | - | cfg, err := telegram.LoadConfig(zotHome) | |
| 355 | + | newAdapter: func(zutHome string) (bot.BotAdapter, error) { | |
| 356 | + | cfg, err := telegram.LoadConfig(zutHome) | |
| 357 | 357 | if err != nil { | |
| 358 | 358 | return nil, err | |
| 359 | 359 | } | |
| 360 | 360 | return telegram.NewAdapter( | |
| 361 | 361 | telegram.NewClient(cfg.BotToken), | |
| 362 | 362 | &cfg, | |
| 363 | - | func(c telegram.Config) error { return telegram.SaveConfig(zotHome, c) }, | |
| 363 | + | func(c telegram.Config) error { return telegram.SaveConfig(zutHome, c) }, | |
| 364 | 364 | ), nil | |
| 365 | 365 | }, | |
| 366 | 366 | } | |
| 367 | 367 | } | |
| 368 | 368 | ||
| 369 | - | // printTelegramBotHelp prints usage for `zot telegram-bot`. | |
| 369 | + | // printTelegramBotHelp prints usage for `zut telegram-bot`. | |
| 370 | 370 | func printTelegramBotHelp() { | |
| 371 | - | fmt.Fprint(os.Stderr, `zot telegram-bot — telegram bridge | |
| 371 | + | fmt.Fprint(os.Stderr, `zut telegram-bot — telegram bridge | |
| 372 | 372 | ||
| 373 | 373 | usage: | |
| 374 | - | zot telegram-bot setup paste a BotFather token, verify, save | |
| 375 | - | zot telegram-bot status show bridge config and whether it's running | |
| 376 | - | zot telegram-bot run [flags] run in the foreground (ctrl+c to stop) | |
| 377 | - | zot telegram-bot start [flags] launch in background, detach, return immediately | |
| 378 | - | zot telegram-bot stop sigterm the running background bot, sigkill if needed | |
| 379 | - | zot telegram-bot logs [--follow] tail the background bot's log file | |
| 380 | - | zot telegram-bot reset forget token + allowed user | |
| 374 | + | zut telegram-bot setup paste a BotFather token, verify, save | |
| 375 | + | zut telegram-bot status show bridge config and whether it's running | |
| 376 | + | zut telegram-bot run [flags] run in the foreground (ctrl+c to stop) | |
| 377 | + | zut telegram-bot start [flags] launch in background, detach, return immediately | |
| 378 | + | zut telegram-bot stop sigterm the running background bot, sigkill if needed | |
| 379 | + | zut telegram-bot logs [--follow] tail the background bot's log file | |
| 380 | + | zut telegram-bot reset forget token + allowed user | |
| 381 | 381 | ||
| 382 | 382 | setup flow: | |
| 383 | 383 | 1. talk to @BotFather on telegram, /newbot, copy the token | |
| 384 | - | 2. run "zot telegram-bot setup" and paste the token | |
| 385 | - | 3. run "zot telegram-bot start" (background) or "zot telegram-bot run" (foreground) | |
| 384 | + | 2. run "zut telegram-bot setup" and paste the token | |
| 385 | + | 3. run "zut telegram-bot start" (background) or "zut telegram-bot run" (foreground) | |
| 386 | 386 | 4. send /start to your bot from telegram; the first sender claims it | |
| 387 | 387 | ||
| 388 | 388 | while the bot is running, dm it anything and the message is forwarded | |
| @@ -391,9 +391,9 @@ to the agent the same way it would be from the tui. image attachments | |||
| 391 | 391 | telegram commands the bot handles directly: /help, /status, /stop. | |
| 392 | 392 | ||
| 393 | 393 | config & state: | |
| 394 | - | $ZOT_HOME/bot.json # bot token + paired user (mode 0600) | |
| 395 | - | $ZOT_HOME/bot.pid # pid of the running bot (written by run/start) | |
| 396 | - | $ZOT_HOME/logs/bot.log # stdout+stderr from "zot telegram-bot start" | |
| 394 | + | $ZUT_HOME/bot.json # bot token + paired user (mode 0600) | |
| 395 | + | $ZUT_HOME/bot.pid # pid of the running bot (written by run/start) | |
| 396 | + | $ZUT_HOME/logs/bot.log # stdout+stderr from "zut telegram-bot start" | |
| 397 | 397 | `) | |
| 398 | 398 | } | |
| 399 | 399 | ||
| @@ -422,7 +422,7 @@ func readBotToken(in *os.File, out io.Writer) (string, error) { | |||
| 422 | 422 | // telegramBotSetup interactively reads a bot token without echoing it on a | |
| 423 | 423 | // terminal, verifies it via getMe, and saves it. | |
| 424 | 424 | func telegramBotSetup(_ []string) error { | |
| 425 | - | cfg, err := telegram.LoadConfig(ZotHome()) | |
| 425 | + | cfg, err := telegram.LoadConfig(ZutHome()) | |
| 426 | 426 | if err != nil { | |
| 427 | 427 | return err | |
| 428 | 428 | } | |
| @@ -448,23 +448,23 @@ func telegramBotSetup(_ []string) error { | |||
| 448 | 448 | // Any stored pairing might be for a different bot; clear it. | |
| 449 | 449 | cfg.AllowedUserID = 0 | |
| 450 | 450 | cfg.LastUpdateID = 0 | |
| 451 | - | if err := telegram.SaveConfig(ZotHome(), cfg); err != nil { | |
| 451 | + | if err := telegram.SaveConfig(ZutHome(), cfg); err != nil { | |
| 452 | 452 | return err | |
| 453 | 453 | } | |
| 454 | - | fmt.Printf("\nsaved: @%s (id=%d) to %s\n", me.Username, me.ID, telegram.ConfigPath(ZotHome())) | |
| 455 | - | fmt.Println("next: run `zot telegram-bot run`, then send /start to your bot from telegram.") | |
| 454 | + | fmt.Printf("\nsaved: @%s (id=%d) to %s\n", me.Username, me.ID, telegram.ConfigPath(ZutHome())) | |
| 455 | + | fmt.Println("next: run `zut telegram-bot run`, then send /start to your bot from telegram.") | |
| 456 | 456 | return nil | |
| 457 | 457 | } | |
| 458 | 458 | ||
| 459 | 459 | // telegramBotStatus prints the current bot config without the token, plus | |
| 460 | 460 | // whether the background process is alive. | |
| 461 | 461 | func telegramBotStatus() error { | |
| 462 | - | cfg, err := telegram.LoadConfig(ZotHome()) | |
| 462 | + | cfg, err := telegram.LoadConfig(ZutHome()) | |
| 463 | 463 | if err != nil { | |
| 464 | 464 | return err | |
| 465 | 465 | } | |
| 466 | 466 | if cfg.BotToken == "" { | |
| 467 | - | fmt.Println("telegram: not configured (run `zot telegram-bot setup`)") | |
| 467 | + | fmt.Println("telegram: not configured (run `zut telegram-bot setup`)") | |
| 468 | 468 | return nil | |
| 469 | 469 | } | |
| 470 | 470 | maskedTok := maskToken(cfg.BotToken) | |
| @@ -476,18 +476,18 @@ func telegramBotStatus() error { | |||
| 476 | 476 | fmt.Printf("paired with: telegram user id %d\n", cfg.AllowedUserID) | |
| 477 | 477 | } | |
| 478 | 478 | fmt.Printf("last update: %d\n", cfg.LastUpdateID) | |
| 479 | - | fmt.Printf("config file: %s\n", telegram.ConfigPath(ZotHome())) | |
| 479 | + | fmt.Printf("config file: %s\n", telegram.ConfigPath(ZutHome())) | |
| 480 | 480 | ||
| 481 | - | pid, alive, _ := telegram.IsRunning(ZotHome()) | |
| 481 | + | pid, alive, _ := telegram.IsRunning(ZutHome()) | |
| 482 | 482 | switch { | |
| 483 | 483 | case alive: | |
| 484 | 484 | fmt.Printf("process: running (pid %d)\n", pid) | |
| 485 | 485 | case pid > 0: | |
| 486 | - | fmt.Printf("process: stopped (stale pid %d in %s)\n", pid, telegram.PIDPath(ZotHome())) | |
| 486 | + | fmt.Printf("process: stopped (stale pid %d in %s)\n", pid, telegram.PIDPath(ZutHome())) | |
| 487 | 487 | default: | |
| 488 | 488 | fmt.Println("process: stopped") | |
| 489 | 489 | } | |
| 490 | - | logPath := telegram.LogPath(ZotHome()) | |
| 490 | + | logPath := telegram.LogPath(ZutHome()) | |
| 491 | 491 | if fi, err := os.Stat(logPath); err == nil { | |
| 492 | 492 | fmt.Printf("log file: %s (%d bytes)\n", logPath, fi.Size()) | |
| 493 | 493 | } | |
| @@ -496,7 +496,7 @@ func telegramBotStatus() error { | |||
| 496 | 496 | ||
| 497 | 497 | // telegramBotReset wipes the on-disk bot.json entry. | |
| 498 | 498 | func telegramBotReset() error { | |
| 499 | - | p := telegram.ConfigPath(ZotHome()) | |
| 499 | + | p := telegram.ConfigPath(ZutHome()) | |
| 500 | 500 | if _, err := os.Stat(p); os.IsNotExist(err) { | |
| 501 | 501 | fmt.Println("no bot config to reset") | |
| 502 | 502 | return nil | |
M packages/agent/botspec.go +5 -5
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | package agent | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "github.com/patriceckhart/zot/packages/agent/modes/bot" | |
| 4 | + | "github.com/bnema/zut/packages/agent/modes/bot" | |
| 5 | 5 | ) | |
| 6 | 6 | ||
| 7 | 7 | // botSpec describes one bot protocol to the generic daemon CLI in | |
| @@ -13,12 +13,12 @@ type botSpec struct { | |||
| 13 | 13 | subcommand string // "telegram-bot", "matrix-bot" | |
| 14 | 14 | aliases []string // {"tg"}, {"mx"} | |
| 15 | 15 | ||
| 16 | - | pidPath func(zotHome string) string | |
| 17 | - | logPath func(zotHome string) string | |
| 16 | + | pidPath func(zutHome string) string | |
| 17 | + | logPath func(zutHome string) string | |
| 18 | 18 | ||
| 19 | 19 | // configured reports whether setup has been completed (e.g. a | |
| 20 | 20 | // token is present), with a hint error message when it hasn't. | |
| 21 | - | configured func(zotHome string) (bool, error) | |
| 21 | + | configured func(zutHome string) (bool, error) | |
| 22 | 22 | ||
| 23 | 23 | printHelp func() | |
| 24 | 24 | setup func(tail []string) error | |
| @@ -27,7 +27,7 @@ type botSpec struct { | |||
| 27 | 27 | ||
| 28 | 28 | // newAdapter builds the protocol adapter for the standalone | |
| 29 | 29 | // daemon. Credential refresh stays generic in botRun. | |
| 30 | - | newAdapter func(zotHome string) (bot.BotAdapter, error) | |
| 30 | + | newAdapter func(zutHome string) (bot.BotAdapter, error) | |
| 31 | 31 | } | |
| 32 | 32 | ||
| 33 | 33 | // botSpecs is the registry the dispatcher walks. Matrix is appended | |
M packages/agent/build.go +31 -31
| @@ -8,13 +8,13 @@ import ( | |||
| 8 | 8 | "runtime" | |
| 9 | 9 | "strings" | |
| 10 | 10 | ||
| 11 | - | zotdocs "github.com/patriceckhart/zot" | |
| 12 | - | "github.com/patriceckhart/zot/packages/agent/lsp" | |
| 13 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 14 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 15 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 16 | - | "github.com/patriceckhart/zot/packages/core" | |
| 17 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | zutdocs "github.com/bnema/zut" | |
| 12 | + | "github.com/bnema/zut/packages/agent/lsp" | |
| 13 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 14 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 15 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 16 | + | "github.com/bnema/zut/packages/core" | |
| 17 | + | "github.com/bnema/zut/packages/provider" | |
| 18 | 18 | ) | |
| 19 | 19 | ||
| 20 | 20 | // ContextFile is an instruction file loaded into the system prompt. | |
| @@ -136,7 +136,7 @@ func (r *Resolved) MergeExtensionTools(mgr ExtensionToolSource) { | |||
| 136 | 136 | Tools: r.ToolSummary, | |
| 137 | 137 | Custom: r.systemCustom, | |
| 138 | 138 | Append: r.systemAppend, | |
| 139 | - | ZotDocsDir: filepath.Join(ZotHome(), "docs"), | |
| 139 | + | ZutDocsDir: filepath.Join(ZutHome(), "docs"), | |
| 140 | 140 | }) | |
| 141 | 141 | } | |
| 142 | 142 | ||
| @@ -217,7 +217,7 @@ func toolSummariesFromRegistry(reg core.Registry, cached map[string]string) []To | |||
| 217 | 217 | return out | |
| 218 | 218 | } | |
| 219 | 219 | ||
| 220 | - | // defaultModelForProvider returns the model id zot prefers when the | |
| 220 | + | // defaultModelForProvider returns the model id zut prefers when the | |
| 221 | 221 | // caller didn't pick one. Mirrors the per-provider switch used at | |
| 222 | 222 | // multiple points in Resolve; centralised so the unknown-model | |
| 223 | 223 | // recovery path and the no-model-configured path can't drift. | |
| @@ -289,7 +289,7 @@ func defaultModelForProvider(prov string) string { | |||
| 289 | 289 | } | |
| 290 | 290 | } | |
| 291 | 291 | ||
| 292 | - | // knownProviders is the set of provider ids zot recognises. Used by | |
| 292 | + | // knownProviders is the set of provider ids zut recognises. Used by | |
| 293 | 293 | // Resolve to validate args.Provider, by extension-callers, and by the | |
| 294 | 294 | // auto-fallback logic that picks any logged-in provider when the user's | |
| 295 | 295 | // preferred one has no credentials. | |
| @@ -495,7 +495,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 495 | 495 | // If the user did NOT explicitly pick a provider (neither via --provider | |
| 496 | 496 | // nor by saving one in config.json) and the default one has no | |
| 497 | 497 | // credentials, auto-fall-back to whichever provider is actually logged | |
| 498 | - | // in. That way running plain `zot` after `/login` (any provider) never | |
| 498 | + | // in. That way running plain `zut` after `/login` (any provider) never | |
| 499 | 499 | // shows a "not logged in" banner. | |
| 500 | 500 | // | |
| 501 | 501 | // Critical: when the user HAS saved a provider in config.json (e.g. | |
| @@ -518,7 +518,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 518 | 518 | // env-based credential is discovered, e.g. an env-only | |
| 519 | 519 | // amazon-bedrock setup (AWS_BEARER_TOKEN_BEDROCK / AWS_PROFILE / | |
| 520 | 520 | // IAM keys) when no config.json pins the provider, such as after | |
| 521 | - | // pointing ZOT_HOME at a fresh home dir. Iteration order of | |
| 521 | + | // pointing ZUT_HOME at a fresh home dir. Iteration order of | |
| 522 | 522 | // knownProviders defines fallback priority. Local providers without a | |
| 523 | 523 | // default model are skipped because selecting either one here would | |
| 524 | 524 | // fail before the user can choose a model. | |
| @@ -591,7 +591,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 591 | 591 | if err != nil { | |
| 592 | 592 | // The model the user (or persisted config) asked for is no | |
| 593 | 593 | // longer in the active catalogue — they probably removed it | |
| 594 | - | // from their models.json or upgraded zot and the id changed. | |
| 594 | + | // from their models.json or upgraded zut and the id changed. | |
| 595 | 595 | // Refusing to launch is the wrong move: it strands the user | |
| 596 | 596 | // with no way to even open the TUI and pick a new model. | |
| 597 | 597 | // Fall back to the provider's default, warn on stderr, and, | |
| @@ -600,7 +600,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 600 | 600 | // doesn't repeat on every launch. | |
| 601 | 601 | ||
| 602 | 602 | // Gateway providers can accept route-qualified ids that are not in | |
| 603 | - | // zot's local catalog yet, for example OpenRouter's | |
| 603 | + | // zut's local catalog yet, for example OpenRouter's | |
| 604 | 604 | // "deepseek/deepseek-v4-flash". Preserve only route-qualified ids; | |
| 605 | 605 | // plain unknown values are likely typos and should still fall back. | |
| 606 | 606 | if isGatewayProvider(provName) && isGatewayRoutedModelID(model) { | |
| @@ -628,7 +628,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 628 | 628 | } | |
| 629 | 629 | } | |
| 630 | 630 | fmt.Fprintf(os.Stderr, | |
| 631 | - | "zot: model %q is not in the active catalogue; using %q instead. Pick a different model with --model or /model.\n", | |
| 631 | + | "zut: model %q is not in the active catalogue; using %q instead. Pick a different model with --model or /model.\n", | |
| 632 | 632 | model, fm.ID) | |
| 633 | 633 | if args.Model == "" && cfg.Model == model { | |
| 634 | 634 | cfg.Model = fm.ID | |
| @@ -677,7 +677,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 677 | 677 | } | |
| 678 | 678 | ||
| 679 | 679 | if credErr != nil && requireCred { | |
| 680 | - | return Resolved{}, fmt.Errorf("%w; set %s_API_KEY, pass --api-key, or run `zot` and /login", | |
| 680 | + | return Resolved{}, fmt.Errorf("%w; set %s_API_KEY, pass --api-key, or run `zut` and /login", | |
| 681 | 681 | credErr, envVarName(provName)) | |
| 682 | 682 | } | |
| 683 | 683 | ||
| @@ -692,7 +692,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 692 | 692 | lspEnabled := !args.NoLSP && cfg.LSPEnabledFor(subagentSession) | |
| 693 | 693 | reg := buildToolRegistry(args, args.CWD, sandbox, lspEnabled, cfg.LSPDiagnosticsOnWriteEnabled(subagentSession), cfg.LSPDiagnosticsOnEditEnabled(subagentSession)) | |
| 694 | 694 | ||
| 695 | - | docsDir, _ := zotdocs.EnsureInstalled(ZotHome()) | |
| 695 | + | docsDir, _ := zutdocs.EnsureInstalled(ZutHome()) | |
| 696 | 696 | ||
| 697 | 697 | // Skill discovery: scan project + global locations + built-in | |
| 698 | 698 | // skills shipped with the binary. If any are found, register | |
| @@ -711,7 +711,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 711 | 711 | if !args.NoSkill && (selectedProfile == nil || selectedProfile.InheritSkills == nil || *selectedProfile.InheritSkills) { | |
| 712 | 712 | skillsEnabled = true | |
| 713 | 713 | homeDir, _ := os.UserHomeDir() | |
| 714 | - | discovered, _ = skills.Discover(ZotHome(), args.CWD, homeDir, args.WithSkills) | |
| 714 | + | discovered, _ = skills.Discover(ZutHome(), args.CWD, homeDir, args.WithSkills) | |
| 715 | 715 | if len(discovered) > 0 { | |
| 716 | 716 | skillTool = skills.NewTool(discovered) | |
| 717 | 717 | reg[skillTool.Name()] = skillTool | |
| @@ -724,7 +724,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 724 | 724 | ||
| 725 | 725 | contextFiles := []ContextFile(nil) | |
| 726 | 726 | if selectedProfile == nil || selectedProfile.InheritProjectContext == nil || *selectedProfile.InheritProjectContext { | |
| 727 | - | contextFiles = loadAgentsContext(args.CWD, ZotHome()) | |
| 727 | + | contextFiles = loadAgentsContext(args.CWD, ZutHome()) | |
| 728 | 728 | } | |
| 729 | 729 | append_ := []string(nil) | |
| 730 | 730 | if cfg.PonytailModeEnabled() { | |
| @@ -754,13 +754,13 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 754 | 754 | ||
| 755 | 755 | // Custom system prompt resolution order: | |
| 756 | 756 | // 1. --system-prompt flag (highest priority; ad-hoc per run) | |
| 757 | - | // 2. $ZOT_HOME/SYSTEM.md (persistent user override) | |
| 757 | + | // 2. $ZUT_HOME/SYSTEM.md (persistent user override) | |
| 758 | 758 | // 3. built-in default (defaultIdentity + defaultGuidelines) | |
| 759 | 759 | custom := args.SystemPrompt | |
| 760 | 760 | if selectedProfile != nil && selectedProfile.SystemPromptMode == "replace" { | |
| 761 | 761 | custom = selectedProfile.SystemPrompt | |
| 762 | 762 | } else if custom == "" { | |
| 763 | - | custom = readUserSystemPrompt(ZotHome()) | |
| 763 | + | custom = readUserSystemPrompt(ZutHome()) | |
| 764 | 764 | } | |
| 765 | 765 | ||
| 766 | 766 | sys := BuildSystemPrompt(SystemPromptOpts{ | |
| @@ -768,7 +768,7 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 768 | 768 | Tools: summaries, | |
| 769 | 769 | Custom: custom, | |
| 770 | 770 | Append: append_, | |
| 771 | - | ZotDocsDir: docsDir, | |
| 771 | + | ZutDocsDir: docsDir, | |
| 772 | 772 | }) | |
| 773 | 773 | ||
| 774 | 774 | reasoning := provider.NormalizeReasoning(firstNonEmpty(args.Reasoning, cfg.Reasoning)) | |
| @@ -818,16 +818,16 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 818 | 818 | }, nil | |
| 819 | 819 | } | |
| 820 | 820 | ||
| 821 | - | // readUserSystemPrompt looks for $ZOT_HOME/SYSTEM.md and returns its | |
| 821 | + | // readUserSystemPrompt looks for $ZUT_HOME/SYSTEM.md and returns its | |
| 822 | 822 | // trimmed contents, or "" when the file is missing / unreadable / | |
| 823 | 823 | // empty. Errors are intentionally swallowed: the file is optional, | |
| 824 | 824 | // and any failure to read it should fall back to the built-in | |
| 825 | 825 | // default system prompt rather than crash the run. | |
| 826 | - | func readUserSystemPrompt(zotHome string) string { | |
| 827 | - | if zotHome == "" { | |
| 826 | + | func readUserSystemPrompt(zutHome string) string { | |
| 827 | + | if zutHome == "" { | |
| 828 | 828 | return "" | |
| 829 | 829 | } | |
| 830 | - | path := filepath.Join(zotHome, "SYSTEM.md") | |
| 830 | + | path := filepath.Join(zutHome, "SYSTEM.md") | |
| 831 | 831 | raw, err := os.ReadFile(path) | |
| 832 | 832 | if err != nil { | |
| 833 | 833 | return "" | |
| @@ -836,10 +836,10 @@ func readUserSystemPrompt(zotHome string) string { | |||
| 836 | 836 | } | |
| 837 | 837 | ||
| 838 | 838 | // loadAgentsContext loads optional AGENTS.md instruction files. No | |
| 839 | - | // default file is created or required: zot only includes files that | |
| 840 | - | // already exist. Global instructions ($ZOT_HOME/AGENTS.md) come first, | |
| 839 | + | // default file is created or required: zut only includes files that | |
| 840 | + | // already exist. Global instructions ($ZUT_HOME/AGENTS.md) come first, | |
| 841 | 841 | // followed by project instructions from the filesystem root down to cwd. | |
| 842 | - | func loadAgentsContext(cwd, zotHome string) []ContextFile { | |
| 842 | + | func loadAgentsContext(cwd, zutHome string) []ContextFile { | |
| 843 | 843 | var files []ContextFile | |
| 844 | 844 | seen := map[string]bool{} | |
| 845 | 845 | add := func(path string) { | |
| @@ -877,7 +877,7 @@ func loadAgentsContext(cwd, zotHome string) []ContextFile { | |||
| 877 | 877 | } | |
| 878 | 878 | } | |
| 879 | 879 | ||
| 880 | - | addFirstFromDir(zotHome) | |
| 880 | + | addFirstFromDir(zutHome) | |
| 881 | 881 | ||
| 882 | 882 | if cwd != "" { | |
| 883 | 883 | abs, err := filepath.Abs(cwd) | |
| @@ -1250,7 +1250,7 @@ func kimiCodeHeaders() map[string]string { | |||
| 1250 | 1250 | } | |
| 1251 | 1251 | } | |
| 1252 | 1252 | if deviceID == "" { | |
| 1253 | - | deviceID = "zot" | |
| 1253 | + | deviceID = "zut" | |
| 1254 | 1254 | } | |
| 1255 | 1255 | return map[string]string{ | |
| 1256 | 1256 | "User-Agent": "KimiCLI/1.41.0", | |
M packages/agent/build_test.go +37 -37
| @@ -10,10 +10,10 @@ import ( | |||
| 10 | 10 | "strings" | |
| 11 | 11 | "testing" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 14 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 15 | - | "github.com/patriceckhart/zot/packages/core" | |
| 16 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 14 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 15 | + | "github.com/bnema/zut/packages/core" | |
| 16 | + | "github.com/bnema/zut/packages/provider" | |
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | 19 | type bundledSkillSource struct { | |
| @@ -140,16 +140,16 @@ func TestAutoSubagentsToolPoliciesTrackEachTool(t *testing.T) { | |||
| 140 | 140 | ||
| 141 | 141 | func TestReadAgentsContextLoadsGlobalAndAncestors(t *testing.T) { | |
| 142 | 142 | root := t.TempDir() | |
| 143 | - | zotHome := filepath.Join(root, "zot-home") | |
| 143 | + | zutHome := filepath.Join(root, "zut-home") | |
| 144 | 144 | project := filepath.Join(root, "repo") | |
| 145 | 145 | nested := filepath.Join(project, "packages", "app") | |
| 146 | - | if err := os.MkdirAll(zotHome, 0o755); err != nil { | |
| 146 | + | if err := os.MkdirAll(zutHome, 0o755); err != nil { | |
| 147 | 147 | t.Fatal(err) | |
| 148 | 148 | } | |
| 149 | 149 | if err := os.MkdirAll(nested, 0o755); err != nil { | |
| 150 | 150 | t.Fatal(err) | |
| 151 | 151 | } | |
| 152 | - | if err := os.WriteFile(filepath.Join(zotHome, "AGENTS.md"), []byte("global rule"), 0o644); err != nil { | |
| 152 | + | if err := os.WriteFile(filepath.Join(zutHome, "AGENTS.md"), []byte("global rule"), 0o644); err != nil { | |
| 153 | 153 | t.Fatal(err) | |
| 154 | 154 | } | |
| 155 | 155 | if err := os.WriteFile(filepath.Join(project, "AGENTS.md"), []byte("repo rule"), 0o644); err != nil { | |
| @@ -159,12 +159,12 @@ func TestReadAgentsContextLoadsGlobalAndAncestors(t *testing.T) { | |||
| 159 | 159 | t.Fatal(err) | |
| 160 | 160 | } | |
| 161 | 161 | ||
| 162 | - | files := loadAgentsContext(nested, zotHome) | |
| 162 | + | files := loadAgentsContext(nested, zutHome) | |
| 163 | 163 | if len(files) != 3 { | |
| 164 | 164 | t.Fatalf("loaded %d context files, want 3: %#v", len(files), files) | |
| 165 | 165 | } | |
| 166 | 166 | wantPaths := []string{ | |
| 167 | - | filepath.Join(zotHome, "AGENTS.md"), | |
| 167 | + | filepath.Join(zutHome, "AGENTS.md"), | |
| 168 | 168 | filepath.Join(project, "AGENTS.md"), | |
| 169 | 169 | filepath.Join(nested, "AGENTS.md"), | |
| 170 | 170 | } | |
| @@ -190,7 +190,7 @@ func TestFindSubagentProfileReportsDiscoveryFailure(t *testing.T) { | |||
| 190 | 190 | if err := os.WriteFile(profileSource, []byte("not a directory"), 0o600); err != nil { | |
| 191 | 191 | t.Fatal(err) | |
| 192 | 192 | } | |
| 193 | - | t.Setenv("ZOT_AGENT_PROFILES", profileSource) | |
| 193 | + | t.Setenv("ZUT_AGENT_PROFILES", profileSource) | |
| 194 | 194 | ||
| 195 | 195 | profile, err := findSubagentProfile("", "reviewer") | |
| 196 | 196 | if profile != nil { | |
| @@ -207,7 +207,7 @@ func TestFindSubagentProfileReportsDiscoveryFailure(t *testing.T) { | |||
| 207 | 207 | func TestResolveIncludesNamedSubagentsListWhenAutoSubagentsIsEnabled(t *testing.T) { | |
| 208 | 208 | root := t.TempDir() | |
| 209 | 209 | home := filepath.Join(root, "home") | |
| 210 | - | zotHome := filepath.Join(root, "zot-home") | |
| 210 | + | zutHome := filepath.Join(root, "zut-home") | |
| 211 | 211 | project := filepath.Join(root, "repo") | |
| 212 | 212 | if err := os.MkdirAll(filepath.Join(home, ".agents", "agents"), 0o755); err != nil { | |
| 213 | 213 | t.Fatal(err) | |
| @@ -216,8 +216,8 @@ func TestResolveIncludesNamedSubagentsListWhenAutoSubagentsIsEnabled(t *testing. | |||
| 216 | 216 | t.Fatal(err) | |
| 217 | 217 | } | |
| 218 | 218 | t.Setenv("HOME", home) | |
| 219 | - | t.Setenv("ZOT_HOME", zotHome) | |
| 220 | - | t.Setenv("ZOT_AGENT_PROFILES", filepath.Join(home, ".agents", "agents")) | |
| 219 | + | t.Setenv("ZUT_HOME", zutHome) | |
| 220 | + | t.Setenv("ZUT_AGENT_PROFILES", filepath.Join(home, ".agents", "agents")) | |
| 221 | 221 | enabled := true | |
| 222 | 222 | if err := SaveConfig(Config{Provider: "openai", Model: "gpt-5", AutoSubagentsEnabled: &enabled}); err != nil { | |
| 223 | 223 | t.Fatal(err) | |
| @@ -247,7 +247,7 @@ Review without editing. | |||
| 247 | 247 | func TestResolveAppliesSelectedSubagentProfile(t *testing.T) { | |
| 248 | 248 | root := t.TempDir() | |
| 249 | 249 | home := filepath.Join(root, "home") | |
| 250 | - | zotHome := filepath.Join(root, "zot-home") | |
| 250 | + | zutHome := filepath.Join(root, "zut-home") | |
| 251 | 251 | project := filepath.Join(root, "repo") | |
| 252 | 252 | if err := os.MkdirAll(filepath.Join(home, ".agents", "agents"), 0o755); err != nil { | |
| 253 | 253 | t.Fatal(err) | |
| @@ -256,12 +256,12 @@ func TestResolveAppliesSelectedSubagentProfile(t *testing.T) { | |||
| 256 | 256 | t.Fatal(err) | |
| 257 | 257 | } | |
| 258 | 258 | t.Setenv("HOME", home) | |
| 259 | - | t.Setenv("ZOT_HOME", zotHome) | |
| 260 | - | t.Setenv("ZOT_AGENT_PROFILES", filepath.Join(home, ".agents", "agents")) | |
| 259 | + | t.Setenv("ZUT_HOME", zutHome) | |
| 260 | + | t.Setenv("ZUT_AGENT_PROFILES", filepath.Join(home, ".agents", "agents")) | |
| 261 | 261 | if err := SaveConfig(Config{Provider: "openai", Model: "gpt-5", Reasoning: "high"}); err != nil { | |
| 262 | 262 | t.Fatal(err) | |
| 263 | 263 | } | |
| 264 | - | if err := os.WriteFile(filepath.Join(zotHome, "AGENTS.md"), []byte("global context"), 0o600); err != nil { | |
| 264 | + | if err := os.WriteFile(filepath.Join(zutHome, "AGENTS.md"), []byte("global context"), 0o600); err != nil { | |
| 265 | 265 | t.Fatal(err) | |
| 266 | 266 | } | |
| 267 | 267 | profile := `--- | |
| @@ -320,7 +320,7 @@ func TestResolveSubagentFastModeUsesHostSettingAsAnUpperBound(t *testing.T) { | |||
| 320 | 320 | t.Run(tc.name, func(t *testing.T) { | |
| 321 | 321 | root := t.TempDir() | |
| 322 | 322 | home := filepath.Join(root, "home") | |
| 323 | - | zotHome := filepath.Join(root, "zot-home") | |
| 323 | + | zutHome := filepath.Join(root, "zut-home") | |
| 324 | 324 | project := filepath.Join(root, "repo") | |
| 325 | 325 | profilesDir := filepath.Join(home, ".agents", "agents") | |
| 326 | 326 | if err := os.MkdirAll(profilesDir, 0o755); err != nil { | |
| @@ -330,8 +330,8 @@ func TestResolveSubagentFastModeUsesHostSettingAsAnUpperBound(t *testing.T) { | |||
| 330 | 330 | t.Fatal(err) | |
| 331 | 331 | } | |
| 332 | 332 | t.Setenv("HOME", home) | |
| 333 | - | t.Setenv("ZOT_HOME", zotHome) | |
| 334 | - | t.Setenv("ZOT_AGENT_PROFILES", profilesDir) | |
| 333 | + | t.Setenv("ZUT_HOME", zutHome) | |
| 334 | + | t.Setenv("ZUT_AGENT_PROFILES", profilesDir) | |
| 335 | 335 | ||
| 336 | 336 | cfg := Config{Provider: "openai", Model: "gpt-5"} | |
| 337 | 337 | cfg.FastMode = &tc.hostFastMode | |
| @@ -376,12 +376,12 @@ func TestReadAgentsContextMissingFilesIsEmpty(t *testing.T) { | |||
| 376 | 376 | // TestResolveFallsBackWhenConfiguredModelIsGone reproduces the | |
| 377 | 377 | // startup failure caught by the user's screenshot: the persisted | |
| 378 | 378 | // config.json points at a model id that's no longer in the active | |
| 379 | - | // catalogue (because they edited models.json or zot's bundled | |
| 379 | + | // catalogue (because they edited models.json or zut's bundled | |
| 380 | 380 | // catalogue changed). Resolve must NOT error — strands the user | |
| 381 | 381 | // with no way to fix it from the TUI — and should repair the config | |
| 382 | 382 | // so the next launch is silent. | |
| 383 | 383 | func TestResolveFallsBackWhenConfiguredModelIsGone(t *testing.T) { | |
| 384 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 384 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 385 | 385 | t.Setenv("OPENAI_API_KEY", "test-key") | |
| 386 | 386 | // Persist a stale model id. | |
| 387 | 387 | stale := "gpt-5.5-pro-not-real" | |
| @@ -421,7 +421,7 @@ func TestResolveAppliesJailByDefault(t *testing.T) { | |||
| 421 | 421 | {name: "enabled starts locked", config: Config{Provider: "openai", Model: "gpt-5", JailByDefault: boolPtr(true)}, want: true}, | |
| 422 | 422 | } { | |
| 423 | 423 | t.Run(tc.name, func(t *testing.T) { | |
| 424 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 424 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 425 | 425 | t.Setenv("OPENAI_API_KEY", "test-key") | |
| 426 | 426 | if err := SaveConfig(tc.config); err != nil { | |
| 427 | 427 | t.Fatal(err) | |
| @@ -445,7 +445,7 @@ func boolPtr(v bool) *bool { return &v } | |||
| 445 | 445 | // persisted config. If the user passed --model X explicitly and X is | |
| 446 | 446 | // unknown, we still fall back, but we don't touch their config. | |
| 447 | 447 | func TestResolveExplicitFlagStaleDoesNotRepairConfig(t *testing.T) { | |
| 448 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 448 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 449 | 449 | t.Setenv("OPENAI_API_KEY", "test-key") | |
| 450 | 450 | good := "gpt-5" | |
| 451 | 451 | if err := SaveConfig(Config{Provider: "openai", Model: good}); err != nil { | |
| @@ -466,7 +466,7 @@ func TestResolveExplicitFlagStaleDoesNotRepairConfig(t *testing.T) { | |||
| 466 | 466 | } | |
| 467 | 467 | ||
| 468 | 468 | func TestResolveOpenRouterPreservesSavedRoutedModelID(t *testing.T) { | |
| 469 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 469 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 470 | 470 | t.Setenv("OPENROUTER_API_KEY", "test-key") | |
| 471 | 471 | want := "deepseek/deepseek-v4-flash" | |
| 472 | 472 | if err := SaveConfig(Config{Provider: "openrouter", Model: want}); err != nil { | |
| @@ -489,7 +489,7 @@ func TestResolveOpenRouterPreservesSavedRoutedModelID(t *testing.T) { | |||
| 489 | 489 | } | |
| 490 | 490 | ||
| 491 | 491 | func TestResolveGatewayPlainUnknownModelFallsBack(t *testing.T) { | |
| 492 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 492 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 493 | 493 | t.Setenv("OPENROUTER_API_KEY", "test-key") | |
| 494 | 494 | stale := "not-a-routed-model" | |
| 495 | 495 | if err := SaveConfig(Config{Provider: "openrouter", Model: stale}); err != nil { | |
| @@ -509,12 +509,12 @@ func TestResolveGatewayPlainUnknownModelFallsBack(t *testing.T) { | |||
| 509 | 509 | } | |
| 510 | 510 | ||
| 511 | 511 | // TestResolveEnvOnlyBedrockDiscoveredWithoutConfig reproduces issue | |
| 512 | - | // #15: pointing ZOT_HOME at a fresh dir drops the persisted | |
| 512 | + | // #15: pointing ZUT_HOME at a fresh dir drops the persisted | |
| 513 | 513 | // config.json (which pinned provider=amazon-bedrock). Resolve must | |
| 514 | 514 | // still discover bedrock from the AWS env vars instead of falling back | |
| 515 | 515 | // to anthropic and reporting "not logged in". | |
| 516 | 516 | func TestResolveEnvOnlyBedrockDiscoveredWithoutConfig(t *testing.T) { | |
| 517 | - | t.Setenv("ZOT_HOME", t.TempDir()) // fresh home: no config.json | |
| 517 | + | t.Setenv("ZUT_HOME", t.TempDir()) // fresh home: no config.json | |
| 518 | 518 | // Disable the Kimi CLI token fallback so a developer machine with a | |
| 519 | 519 | // real Kimi CLI login doesn't pre-empt bedrock in the scan. | |
| 520 | 520 | if err := SetKimiCLIFallbackDisabled(true); err != nil { | |
| @@ -540,7 +540,7 @@ func TestResolveEnvOnlyBedrockDiscoveredWithoutConfig(t *testing.T) { | |||
| 540 | 540 | } | |
| 541 | 541 | ||
| 542 | 542 | func TestResolveOllamaUsesModelBaseURLBeforeDefault(t *testing.T) { | |
| 543 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 543 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 544 | 544 | provider.SetLiveModels(nil) | |
| 545 | 545 | defer provider.SetLiveModels(nil) | |
| 546 | 546 | provider.SetUserModels([]provider.Model{{ | |
| @@ -562,7 +562,7 @@ func TestResolveOllamaUsesModelBaseURLBeforeDefault(t *testing.T) { | |||
| 562 | 562 | } | |
| 563 | 563 | ||
| 564 | 564 | func TestResolveUsesInheritedSupervisorCredential(t *testing.T) { | |
| 565 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 565 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 566 | 566 | t.Setenv("OPENAI_API_KEY", "") | |
| 567 | 567 | ||
| 568 | 568 | r, err := Resolve(Args{ | |
| @@ -581,7 +581,7 @@ func TestResolveUsesInheritedSupervisorCredential(t *testing.T) { | |||
| 581 | 581 | } | |
| 582 | 582 | ||
| 583 | 583 | func TestResolveLlamaCPPUsesRouterInferenceURL(t *testing.T) { | |
| 584 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 584 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 585 | 585 | t.Setenv("LLAMA_BASE_URL", "http://127.0.0.1:8080/v1/") | |
| 586 | 586 | t.Setenv("LLAMA_API_KEY", "") | |
| 587 | 587 | provider.SetManagedModels(nil) | |
| @@ -603,7 +603,7 @@ func TestResolveLlamaCPPUsesRouterInferenceURL(t *testing.T) { | |||
| 603 | 603 | } | |
| 604 | 604 | ||
| 605 | 605 | func TestResolveLlamaCPPUsesStoredLogin(t *testing.T) { | |
| 606 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 606 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 607 | 607 | t.Setenv("LLAMA_BASE_URL", "") | |
| 608 | 608 | t.Setenv("LLAMA_API_KEY", "") | |
| 609 | 609 | if err := AuthStoreFor().SetEndpointCredential("llama.cpp", "http://localhost:9090", "stored-key"); err != nil { | |
| @@ -620,7 +620,7 @@ func TestResolveLlamaCPPUsesStoredLogin(t *testing.T) { | |||
| 620 | 620 | } | |
| 621 | 621 | ||
| 622 | 622 | func TestResolveCustomProviderModelBaseURLBeatsProviderBaseURL(t *testing.T) { | |
| 623 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 623 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 624 | 624 | t.Setenv("MY_COMPANY_API_KEY", "test-key") | |
| 625 | 625 | path := filepath.Join(t.TempDir(), "models.json") | |
| 626 | 626 | if err := os.WriteFile(path, []byte(`{ | |
| @@ -702,7 +702,7 @@ func TestCustomProviderUsesOpenAIResponsesAPI(t *testing.T) { | |||
| 702 | 702 | } | |
| 703 | 703 | ||
| 704 | 704 | func TestResolveCustomProviderInsecureFromModelsJSONBaseURL(t *testing.T) { | |
| 705 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 705 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 706 | 706 | t.Setenv("LOCAL_PROXY_API_KEY", "test-key") | |
| 707 | 707 | path := filepath.Join(t.TempDir(), "models.json") | |
| 708 | 708 | if err := os.WriteFile(path, []byte(`{ | |
| @@ -734,7 +734,7 @@ func TestResolveCustomProviderInsecureFromModelsJSONBaseURL(t *testing.T) { | |||
| 734 | 734 | } | |
| 735 | 735 | ||
| 736 | 736 | func TestResolveOllamaFallsBackToDefaultBaseURL(t *testing.T) { | |
| 737 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 737 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 738 | 738 | provider.SetLiveModels(nil) | |
| 739 | 739 | defer provider.SetLiveModels(nil) | |
| 740 | 740 | ||
| @@ -785,7 +785,7 @@ func TestResolveInsecureOnlyWithExplicitBaseURL(t *testing.T) { | |||
| 785 | 785 | orig := http.DefaultTransport | |
| 786 | 786 | t.Cleanup(func() { http.DefaultTransport = orig }) | |
| 787 | 787 | ||
| 788 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 788 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 789 | 789 | t.Setenv("OPENAI_API_KEY", "test-key") | |
| 790 | 790 | ||
| 791 | 791 | resolved, err := Resolve(Args{Provider: "moonshotai", InsecureTLS: true}, false) | |
| @@ -811,7 +811,7 @@ func TestResolveInsecureFromConfigRequiresExplicitBaseURL(t *testing.T) { | |||
| 811 | 811 | orig := http.DefaultTransport | |
| 812 | 812 | t.Cleanup(func() { http.DefaultTransport = orig }) | |
| 813 | 813 | ||
| 814 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 814 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 815 | 815 | t.Setenv("OPENAI_API_KEY", "test-key") | |
| 816 | 816 | if err := SaveConfig(Config{Provider: "openai", Insecure: true}); err != nil { | |
| 817 | 817 | t.Fatal(err) | |
M packages/agent/changelog.go +1 -1
| @@ -45,7 +45,7 @@ func FetchChangelog(ctx context.Context, version string) (ChangelogInfo, error) | |||
| 45 | 45 | if !strings.HasPrefix(tag, "v") { | |
| 46 | 46 | tag = "v" + tag | |
| 47 | 47 | } | |
| 48 | - | url := fmt.Sprintf("https://api.github.com/repos/patriceckhart/zot/releases/tags/%s", tag) | |
| 48 | + | url := fmt.Sprintf("https://api.github.com/repos/bnema/zut/releases/tags/%s", tag) | |
| 49 | 49 | req, err := http.NewRequestWithContext(ctx, "GET", url, nil) | |
| 50 | 50 | if err != nil { | |
| 51 | 51 | return ChangelogInfo{}, err | |
M packages/agent/cli.go +45 -45
| @@ -16,16 +16,16 @@ import ( | |||
| 16 | 16 | "syscall" | |
| 17 | 17 | "time" | |
| 18 | 18 | ||
| 19 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 20 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 21 | - | "github.com/patriceckhart/zot/packages/agent/modes" | |
| 22 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 23 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 24 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 25 | - | "github.com/patriceckhart/zot/packages/core" | |
| 26 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 27 | - | "github.com/patriceckhart/zot/packages/provider/auth" | |
| 28 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 19 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 20 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 21 | + | "github.com/bnema/zut/packages/agent/modes" | |
| 22 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 23 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 24 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 25 | + | "github.com/bnema/zut/packages/core" | |
| 26 | + | "github.com/bnema/zut/packages/provider" | |
| 27 | + | "github.com/bnema/zut/packages/provider/auth" | |
| 28 | + | "github.com/bnema/zut/packages/tui" | |
| 29 | 29 | ) | |
| 30 | 30 | ||
| 31 | 31 | // interactiveExtHooks is a tiny adapter that lets the extension | |
| @@ -471,7 +471,7 @@ func prepareSessionResumeWithOptions(path string, current *core.Agent, currentPr | |||
| 471 | 471 | } | |
| 472 | 472 | ||
| 473 | 473 | // Old sessions may have either field absent. Only a complete stored | |
| 474 | - | // selection is actionable; otherwise resume exactly as older zot did. | |
| 474 | + | // selection is actionable; otherwise resume exactly as older zut did. | |
| 475 | 475 | if storedProvider == "" || storedModel == "" { | |
| 476 | 476 | keepSession = true | |
| 477 | 477 | return candidate, nil | |
| @@ -688,7 +688,7 @@ func fanoutAgentEvent(mgr *extensions.Manager, ev core.AgentEvent) { | |||
| 688 | 688 | } | |
| 689 | 689 | } | |
| 690 | 690 | ||
| 691 | - | // Run is the top-level entrypoint for the zot binary. | |
| 691 | + | // Run is the top-level entrypoint for the zut binary. | |
| 692 | 692 | func Run(rawArgs []string, version string) error { | |
| 693 | 693 | // Extension installs can invoke external build and clone processes. | |
| 694 | 694 | // Give those processes a cancellation context without changing signal | |
| @@ -700,7 +700,7 @@ func Run(rawArgs []string, version string) error { | |||
| 700 | 700 | } | |
| 701 | 701 | defer stopExtContext() | |
| 702 | 702 | ||
| 703 | - | // Subcommand router: `zot bot ...` is handled separately so the | |
| 703 | + | // Subcommand router: `zut bot ...` is handled separately so the | |
| 704 | 704 | // generic flag parser doesn't reject "bot" as a positional arg. | |
| 705 | 705 | if handled, err := runBotCommand(rawArgs, version); handled { | |
| 706 | 706 | return err | |
| @@ -711,14 +711,14 @@ func Run(rawArgs []string, version string) error { | |||
| 711 | 711 | if handled, err := runUpdateCommand(rawArgs, version); handled { | |
| 712 | 712 | return err | |
| 713 | 713 | } | |
| 714 | - | if handled, err := runZotfileCommand(rawArgs, version); handled { | |
| 714 | + | if handled, err := runZutfileCommand(rawArgs, version); handled { | |
| 715 | 715 | return err | |
| 716 | 716 | } | |
| 717 | 717 | return runWithArgsRaw(rawArgs, version) | |
| 718 | 718 | } | |
| 719 | 719 | ||
| 720 | 720 | func runWithArgsRaw(rawArgs []string, version string) error { | |
| 721 | - | // `zot rpc` is shorthand for `zot --rpc` so third-party apps can | |
| 721 | + | // `zut rpc` is shorthand for `zut --rpc` so third-party apps can | |
| 722 | 722 | // spawn the binary with a clean argv. Strip the leading 'rpc' | |
| 723 | 723 | // token and let the rest flow through the normal arg parser. | |
| 724 | 724 | if len(rawArgs) > 0 && rawArgs[0] == "rpc" { | |
| @@ -735,7 +735,7 @@ func runWithArgsRaw(rawArgs []string, version string) error { | |||
| 735 | 735 | return nil | |
| 736 | 736 | } | |
| 737 | 737 | if args.Version { | |
| 738 | - | fmt.Println("zot", version) | |
| 738 | + | fmt.Println("zut", version) | |
| 739 | 739 | return nil | |
| 740 | 740 | } | |
| 741 | 741 | // Model catalog: load any cached discovery data before we inspect | |
| @@ -797,7 +797,7 @@ func (nonInteractiveExtHooks) ClearWidget(string, string) | |||
| 797 | 797 | // wire tools into the resolved registry, and a cleanup closure to | |
| 798 | 798 | // defer. Mirrors the interactive-mode setup minus the TUI hooks. | |
| 799 | 799 | func setupNonInteractiveExtensions(ctx context.Context, args Args, r *Resolved, version string) (*extensions.Manager, func()) { | |
| 800 | - | extMgr := extensions.New(ZotHome(), r.CWD, version, r.Provider, r.Model, nonInteractiveExtHooks{}) | |
| 800 | + | extMgr := extensions.New(ZutHome(), r.CWD, version, r.Provider, r.Model, nonInteractiveExtHooks{}) | |
| 801 | 801 | for _, e := range extMgr.LoadExplicit(ctx, args.Exts) { | |
| 802 | 802 | fmt.Fprintln(os.Stderr, "extension load:", e) | |
| 803 | 803 | } | |
| @@ -925,7 +925,7 @@ func runPrintMode(ctx context.Context, args Args, version string) error { | |||
| 925 | 925 | } | |
| 926 | 926 | ||
| 927 | 927 | start := len(ag.Messages()) | |
| 928 | - | if err := runZotfileStartupPre(ctx, args.StartupPre, r.CWD, r.Sandbox, ag, nil, os.Stderr); err != nil { | |
| 928 | + | if err := runZutfileStartupPre(ctx, args.StartupPre, r.CWD, r.Sandbox, ag, nil, os.Stderr); err != nil { | |
| 929 | 929 | return err | |
| 930 | 930 | } | |
| 931 | 931 | if err := reloadResourcesAfterStartupPre(ctx, args, extMgr, r.Sandbox, ag); err != nil { | |
| @@ -991,7 +991,7 @@ func runStreamMode(ctx context.Context, args Args, version string) error { | |||
| 991 | 991 | ||
| 992 | 992 | start := len(ag.Messages()) | |
| 993 | 993 | preSink, finishPre := newStreamTextSink(os.Stdout) | |
| 994 | - | if err := runZotfileStartupPre(ctx, args.StartupPre, r.CWD, r.Sandbox, ag, preSink, os.Stderr); err != nil { | |
| 994 | + | if err := runZutfileStartupPre(ctx, args.StartupPre, r.CWD, r.Sandbox, ag, preSink, os.Stderr); err != nil { | |
| 995 | 995 | finishPre() | |
| 996 | 996 | return err | |
| 997 | 997 | } | |
| @@ -1098,7 +1098,7 @@ func runJSONMode(ctx context.Context, args Args, version string) error { | |||
| 1098 | 1098 | preSink := func(ev core.AgentEvent) { | |
| 1099 | 1099 | _ = enc.Encode(modes.EventToJSON(ev)) | |
| 1100 | 1100 | } | |
| 1101 | - | if err := runZotfileStartupPre(ctx, args.StartupPre, r.CWD, r.Sandbox, ag, preSink, os.Stderr); err != nil { | |
| 1101 | + | if err := runZutfileStartupPre(ctx, args.StartupPre, r.CWD, r.Sandbox, ag, preSink, os.Stderr); err != nil { | |
| 1102 | 1102 | _ = enc.Encode(map[string]any{"type": "error", "message": err.Error()}) | |
| 1103 | 1103 | return err | |
| 1104 | 1104 | } | |
| @@ -1111,11 +1111,11 @@ func runJSONMode(ctx context.Context, args Args, version string) error { | |||
| 1111 | 1111 | return err | |
| 1112 | 1112 | } | |
| 1113 | 1113 | ||
| 1114 | - | // runZotfileStartupPre runs entry.pre before the main non-interactive prompt. | |
| 1114 | + | // runZutfileStartupPre runs entry.pre before the main non-interactive prompt. | |
| 1115 | 1115 | // "!command" values execute via BashTool; other values are sent as an agent turn. | |
| 1116 | 1116 | // shellOut receives live shell chunks (typically os.Stderr); sink receives | |
| 1117 | 1117 | // agent events for plain-text pre (stream mode wires a live text sink). | |
| 1118 | - | func runZotfileStartupPre(ctx context.Context, pre, cwd string, sandbox *tools.Sandbox, ag *core.Agent, sink func(core.AgentEvent), shellOut io.Writer) error { | |
| 1118 | + | func runZutfileStartupPre(ctx context.Context, pre, cwd string, sandbox *tools.Sandbox, ag *core.Agent, sink func(core.AgentEvent), shellOut io.Writer) error { | |
| 1119 | 1119 | pre = strings.TrimSpace(pre) | |
| 1120 | 1120 | if pre == "" { | |
| 1121 | 1121 | return nil | |
| @@ -1143,7 +1143,7 @@ func runZotfileStartupPre(ctx context.Context, pre, cwd string, sandbox *tools.S | |||
| 1143 | 1143 | bash := &tools.BashTool{CWD: cwd, Sandbox: sandbox} | |
| 1144 | 1144 | res, err := bash.Execute(ctx, raw, progress) | |
| 1145 | 1145 | if err != nil { | |
| 1146 | - | return fmt.Errorf("zotfile entry.pre: %w", err) | |
| 1146 | + | return fmt.Errorf("zutfile entry.pre: %w", err) | |
| 1147 | 1147 | } | |
| 1148 | 1148 | if res.IsError { | |
| 1149 | 1149 | var sb strings.Builder | |
| @@ -1156,12 +1156,12 @@ func runZotfileStartupPre(ctx context.Context, pre, cwd string, sandbox *tools.S | |||
| 1156 | 1156 | if msg == "" { | |
| 1157 | 1157 | msg = "command failed" | |
| 1158 | 1158 | } | |
| 1159 | - | return fmt.Errorf("zotfile entry.pre: %s", msg) | |
| 1159 | + | return fmt.Errorf("zutfile entry.pre: %s", msg) | |
| 1160 | 1160 | } | |
| 1161 | 1161 | return nil | |
| 1162 | 1162 | } | |
| 1163 | 1163 | if ag == nil { | |
| 1164 | - | return fmt.Errorf("zotfile entry.pre requires an agent for non-shell prompts") | |
| 1164 | + | return fmt.Errorf("zutfile entry.pre requires an agent for non-shell prompts") | |
| 1165 | 1165 | } | |
| 1166 | 1166 | if sink == nil { | |
| 1167 | 1167 | sink = func(core.AgentEvent) {} | |
| @@ -1174,7 +1174,7 @@ var errInteractiveAgentChanged = errors.New("interactive agent changed during pr | |||
| 1174 | 1174 | // refreshAgentToolsAndPrompt re-resolves tools (including rediscovered | |
| 1175 | 1175 | // skills and currently loaded extension tools) and updates the live | |
| 1176 | 1176 | // agent's registry and system prompt. Used after /reload-ext and after | |
| 1177 | - | // zotfile entry.pre installs new skills or extensions. | |
| 1177 | + | // zutfile entry.pre installs new skills or extensions. | |
| 1178 | 1178 | // mutateRegistry, if non-nil, can inject session-specific tools (e.g. subagent_spawn). | |
| 1179 | 1179 | // interactive, when non-nil, serializes the final commit with agent replacement. | |
| 1180 | 1180 | func refreshAgentToolsAndPrompt(args Args, sharedSandbox *tools.Sandbox, extToolAdapter ExtensionToolSource, ag *core.Agent, mutateRegistry func(core.Registry) core.Registry, interactive *modes.Interactive) error { | |
| @@ -1289,7 +1289,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 1289 | 1289 | // host after constructing it below so startup alerts can be buffered. | |
| 1290 | 1290 | var iv *modes.Interactive | |
| 1291 | 1291 | extHooks := &interactiveExtHooks{} | |
| 1292 | - | extMgr := extensions.New(ZotHome(), r.CWD, version, r.Provider, r.Model, extHooks) | |
| 1292 | + | extMgr := extensions.New(ZutHome(), r.CWD, version, r.Provider, r.Model, extHooks) | |
| 1293 | 1293 | var startupExtensionErrors []string | |
| 1294 | 1294 | // --ext paths first so they win against installed extensions of | |
| 1295 | 1295 | // the same name (loadOne's first-write-wins semantics). | |
| @@ -1321,7 +1321,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 1321 | 1321 | ||
| 1322 | 1322 | // Build the subagent supervisor BEFORE the agent so the auto-subagents | |
| 1323 | 1323 | // tool can reference it during tool-registry construction. State | |
| 1324 | - | // lives under ZotHome/subagents so per-agent meta/events survive | |
| 1324 | + | // lives under ZutHome/subagents so per-agent meta/events survive | |
| 1325 | 1325 | // restarts; the user can hunt orphaned agents down with | |
| 1326 | 1326 | // `git worktree list` if anything misbehaves. | |
| 1327 | 1327 | // | |
| @@ -1331,7 +1331,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 1331 | 1331 | var subagentsMgr *subagents.Supervisor | |
| 1332 | 1332 | subagentsMgr = subagents.New(subagents.Config{ | |
| 1333 | 1333 | Context: ctx, | |
| 1334 | - | Root: filepath.Join(ZotHome(), "subagents"), | |
| 1334 | + | Root: filepath.Join(ZutHome(), "subagents"), | |
| 1335 | 1335 | RepoRoot: r.CWD, | |
| 1336 | 1336 | Provider: r.Provider, | |
| 1337 | 1337 | FastMode: r.FastMode, | |
| @@ -1375,7 +1375,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 1375 | 1375 | // shows them as detached and the user can resume / remove them. | |
| 1376 | 1376 | _, _ = subagentsMgr.Reload() | |
| 1377 | 1377 | if args.PermissionSet != nil { | |
| 1378 | - | // Packaged zotfile agents have an explicit capability ceiling. A | |
| 1378 | + | // Packaged zutfile agents have an explicit capability ceiling. A | |
| 1379 | 1379 | // child process would otherwise rebuild a fresh unrestricted sandbox, | |
| 1380 | 1380 | // so do not expose either slash or tool-based delegation from this | |
| 1381 | 1381 | // restricted host until worker capability propagation exists. | |
| @@ -1559,7 +1559,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 1559 | 1559 | // rescue (a bad key, a typo'd base URL, or a corporate gateway that | |
| 1560 | 1560 | // only the originally-picked provider needed). Re-resolving without | |
| 1561 | 1561 | // them lets the rescue retry use env vars / auth.json / provider | |
| 1562 | - | // defaults the way zot would have without the overrides. | |
| 1562 | + | // defaults the way zut would have without the overrides. | |
| 1563 | 1563 | buildAgentForRescue := func(providerOverride, modelOverride string) (*core.Agent, string, string, error) { | |
| 1564 | 1564 | next := args | |
| 1565 | 1565 | next.APIKey = "" | |
| @@ -1961,10 +1961,10 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 1961 | 1961 | // Fresh session in the new cwd's bucket. We bypass | |
| 1962 | 1962 | // openOrCreateSession's --continue / --resume branches | |
| 1963 | 1963 | // because /cd's semantics are "start fresh here", matching | |
| 1964 | - | // what relaunching `zot --cwd <path>` would do today. | |
| 1964 | + | // what relaunching `zut --cwd <path>` would do today. | |
| 1965 | 1965 | var newSess *core.Session | |
| 1966 | 1966 | if !args.NoSess { | |
| 1967 | - | newRoot := agentSessionsRoot(ZotHome(), args) | |
| 1967 | + | newRoot := agentSessionsRoot(ZutHome(), args) | |
| 1968 | 1968 | core.PruneEmptySessions(newRoot, absPath) | |
| 1969 | 1969 | var serr error | |
| 1970 | 1970 | newSess, serr = core.NewSession(newRoot, absPath, newProvider, newModel, version) | |
| @@ -2004,7 +2004,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 2004 | 2004 | ||
| 2005 | 2005 | // Push the new state into the running Interactive. | |
| 2006 | 2006 | if iv != nil { | |
| 2007 | - | startupPaths := instructionContextPaths(loadAgentsContext(absPath, ZotHome())) | |
| 2007 | + | startupPaths := instructionContextPaths(loadAgentsContext(absPath, ZutHome())) | |
| 2008 | 2008 | iv.ApplyChangedCWDWithStartupContext(newAg, newProvider, newModel, absPath, startupPaths) | |
| 2009 | 2009 | } | |
| 2010 | 2010 | ||
| @@ -2020,7 +2020,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 2020 | 2020 | ||
| 2021 | 2021 | devBuild := isDevVersion(version) | |
| 2022 | 2022 | if devBuild { | |
| 2023 | - | fmt.Fprintln(os.Stderr, "zot:", devVersionNotice) | |
| 2023 | + | fmt.Fprintln(os.Stderr, "zut:", devVersionNotice) | |
| 2024 | 2024 | } | |
| 2025 | 2025 | term := tui.NewProcTerm() | |
| 2026 | 2026 | ||
| @@ -2033,7 +2033,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 2033 | 2033 | updateCh = make(chan modes.UpdateInfo, 1) | |
| 2034 | 2034 | go func() { | |
| 2035 | 2035 | defer close(updateCh) | |
| 2036 | - | src := <-CheckForUpdateAsync(ZotHome(), version) | |
| 2036 | + | src := <-CheckForUpdateAsync(ZutHome(), version) | |
| 2037 | 2037 | updateCh <- modes.UpdateInfo{ | |
| 2038 | 2038 | Current: src.Current, | |
| 2039 | 2039 | Latest: src.Latest, | |
| @@ -2076,10 +2076,10 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 2076 | 2076 | for idx, s := range initialCfg.QuickModelShortcuts { | |
| 2077 | 2077 | quickModelShortcuts[idx] = modes.QuickModelShortcut{Provider: s.Provider, Model: s.Model} | |
| 2078 | 2078 | } | |
| 2079 | - | theme, _, themeErr := tui.DetectThemeWithCustom(ZotHome(), initialCfg.Theme, 80*time.Millisecond) | |
| 2079 | + | theme, _, themeErr := tui.DetectThemeWithCustom(ZutHome(), initialCfg.Theme, 80*time.Millisecond) | |
| 2080 | 2080 | if themeErr != nil { | |
| 2081 | 2081 | fmt.Fprintln(os.Stderr, "theme load:", themeErr) | |
| 2082 | - | if initialCfg.Theme != "" && !tui.ThemeExists(ZotHome(), initialCfg.Theme) { | |
| 2082 | + | if initialCfg.Theme != "" && !tui.ThemeExists(ZutHome(), initialCfg.Theme) { | |
| 2083 | 2083 | initialCfg.Theme = "" | |
| 2084 | 2084 | _ = SaveConfig(initialCfg) | |
| 2085 | 2085 | } | |
| @@ -2095,7 +2095,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 2095 | 2095 | subagentsMgr.SetActiveSession(sess.ID) | |
| 2096 | 2096 | } | |
| 2097 | 2097 | // Best-effort shutdown on interactive exit: stop all running | |
| 2098 | - | // agents so they don't outlive their parent zot. | |
| 2098 | + | // agents so they don't outlive their parent zut. | |
| 2099 | 2099 | if subagentsMgr != nil { | |
| 2100 | 2100 | defer subagentsMgr.StopAll() | |
| 2101 | 2101 | } | |
| @@ -2154,8 +2154,8 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 2154 | 2154 | StartupExtensionErrors: startupExtensionErrors, | |
| 2155 | 2155 | StartupSkillNames: startupSkillNames(startupSkills), | |
| 2156 | 2156 | ShowInstructionsAtStartup: initialCfg.ShowInstructionsAtStartup, | |
| 2157 | - | ZotHome: ZotHome(), | |
| 2158 | - | SessionsRoot: agentSessionsRoot(ZotHome(), args), | |
| 2157 | + | ZutHome: ZutHome(), | |
| 2158 | + | SessionsRoot: agentSessionsRoot(ZutHome(), args), | |
| 2159 | 2159 | Version: version, | |
| 2160 | 2160 | UpdateInfoChan: updateCh, | |
| 2161 | 2161 | Sandbox: sharedSandbox, | |
| @@ -2281,7 +2281,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 2281 | 2281 | // model still sees them through the system-prompt | |
| 2282 | 2282 | // manifest and the skill tool. | |
| 2283 | 2283 | userHome, _ := os.UserHomeDir() | |
| 2284 | - | list, _ := skills.Discover(ZotHome(), r.CWD, userHome, args.WithSkills) | |
| 2284 | + | list, _ := skills.Discover(ZutHome(), r.CWD, userHome, args.WithSkills) | |
| 2285 | 2285 | list = mergeExtensionSkills(skills.NewTool(list), extMgr.Skills()) | |
| 2286 | 2286 | return skills.VisibleSkills(list) | |
| 2287 | 2287 | }, | |
| @@ -2345,7 +2345,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 2345 | 2345 | }) | |
| 2346 | 2346 | } | |
| 2347 | 2347 | ||
| 2348 | - | // Signal-driven flush: a SIGTERM / SIGHUP to the zot process | |
| 2348 | + | // Signal-driven flush: a SIGTERM / SIGHUP to the zut process | |
| 2349 | 2349 | // (closed terminal window, system shutdown, kill) used to lose | |
| 2350 | 2350 | // the entire in-memory transcript because the deferred post-Run | |
| 2351 | 2351 | // flush below never ran. Per-message persistence above covers | |
| @@ -2419,10 +2419,10 @@ func openOrCreateSession(args Args, r Resolved, ag *core.Agent, version string) | |||
| 2419 | 2419 | if args.NoSess { | |
| 2420 | 2420 | return nil, nil | |
| 2421 | 2421 | } | |
| 2422 | - | // Sweep meta-only files left over from older zot versions (and from | |
| 2422 | + | // Sweep meta-only files left over from older zut versions (and from | |
| 2423 | 2423 | // any session that crashed before its first AppendMessage). Cheap; | |
| 2424 | 2424 | // reads the first few bytes of each file in the cwd's session dir. | |
| 2425 | - | sessionsRoot := agentSessionsRoot(ZotHome(), args) | |
| 2425 | + | sessionsRoot := agentSessionsRoot(ZutHome(), args) | |
| 2426 | 2426 | core.PruneEmptySessions(sessionsRoot, args.CWD) | |
| 2427 | 2427 | var ( | |
| 2428 | 2428 | s *core.Session | |
M packages/agent/cli_alert_test.go +2 -2
| @@ -3,8 +3,8 @@ package agent | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 7 | - | "github.com/patriceckhart/zot/packages/agent/modes" | |
| 6 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 7 | + | "github.com/bnema/zut/packages/agent/modes" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestInteractiveExtHooksBufferStartupChrome(t *testing.T) { | |
M packages/agent/cli_session_test.go +4 -4
| @@ -9,9 +9,9 @@ import ( | |||
| 9 | 9 | "testing" | |
| 10 | 10 | "time" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/agent/modes" | |
| 13 | - | "github.com/patriceckhart/zot/packages/core" | |
| 14 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 12 | + | "github.com/bnema/zut/packages/agent/modes" | |
| 13 | + | "github.com/bnema/zut/packages/core" | |
| 14 | + | "github.com/bnema/zut/packages/provider" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | func TestTrimMessagesForResumeCarriesDeferredToolActivation(t *testing.T) { | |
| @@ -97,7 +97,7 @@ func TestLiveInteractiveAgentFallsBackBeforeInteractiveConstruction(t *testing.T | |||
| 97 | 97 | } | |
| 98 | 98 | ||
| 99 | 99 | func TestPersistModelCallbackDoesNotReenterSessionTransition(t *testing.T) { | |
| 100 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 100 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 101 | 101 | sess, err := core.NewSession(t.TempDir(), t.TempDir(), "old-provider", "old-model", "test") | |
| 102 | 102 | if err != nil { | |
| 103 | 103 | t.Fatal(err) | |
M packages/agent/config.go +25 -25
| @@ -12,8 +12,8 @@ import ( | |||
| 12 | 12 | "strings" | |
| 13 | 13 | "time" | |
| 14 | 14 | ||
| 15 | - | providerpkg "github.com/patriceckhart/zot/packages/provider" | |
| 16 | - | "github.com/patriceckhart/zot/packages/provider/auth" | |
| 15 | + | providerpkg "github.com/bnema/zut/packages/provider" | |
| 16 | + | "github.com/bnema/zut/packages/provider/auth" | |
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | 19 | // QuickModelShortcut is one configured keyboard shortcut slot. | |
| @@ -57,13 +57,13 @@ type Config struct { | |||
| 57 | 57 | // ToolRender selects how tool calls are drawn in interactive mode. | |
| 58 | 58 | // "box" (default, or empty) wraps each call in a bordered panel; | |
| 59 | 59 | // "flat" drops the frame for a quiet header line plus indented, | |
| 60 | - | // frameless output. The ZOT_FLAT_TOOLS env var overrides this when | |
| 60 | + | // frameless output. The ZUT_FLAT_TOOLS env var overrides this when | |
| 61 | 61 | // set ("1"/"true" forces flat, "0"/"false" forces box). | |
| 62 | 62 | ToolRender string `json:"tool_render,omitempty"` | |
| 63 | 63 | ||
| 64 | 64 | // CompactInput renders sent user messages as a single quiet gutter | |
| 65 | 65 | // line instead of a padded, background-tinted bubble. nil/false | |
| 66 | - | // (the default) keeps the bubble. The ZOT_COMPACT_INPUT env var | |
| 66 | + | // (the default) keeps the bubble. The ZUT_COMPACT_INPUT env var | |
| 67 | 67 | // overrides this when set. | |
| 68 | 68 | CompactInput *bool `json:"compact_input,omitempty"` | |
| 69 | 69 | ||
| @@ -71,7 +71,7 @@ type Config struct { | |||
| 71 | 71 | // Ctrl+1..9. Cmd+1..9 may also work on terminals that forward Super. | |
| 72 | 72 | QuickModelShortcuts []QuickModelShortcut `json:"quick_model_shortcuts,omitempty"` | |
| 73 | 73 | ||
| 74 | - | // InlineImagesEnabled controls whether zot draws screenshots inline | |
| 74 | + | // InlineImagesEnabled controls whether zut draws screenshots inline | |
| 75 | 75 | // when the terminal supports an image protocol. nil/missing means | |
| 76 | 76 | // auto (enabled when supported); false disables; true forces the | |
| 77 | 77 | // detected protocol when available. | |
| @@ -174,43 +174,43 @@ type Config struct { | |||
| 174 | 174 | LastChangelogShown string `json:"last_changelog_shown,omitempty"` | |
| 175 | 175 | } | |
| 176 | 176 | ||
| 177 | - | // ZotHome returns $ZOT_HOME or the OS-default data dir. | |
| 177 | + | // ZutHome returns $ZUT_HOME or the OS-default data dir. | |
| 178 | 178 | // | |
| 179 | - | // All zot state (config.json, auth.json, sessions/, logs/) lives under | |
| 179 | + | // All zut state (config.json, auth.json, sessions/, logs/) lives under | |
| 180 | 180 | // this directory. | |
| 181 | - | func ZotHome() string { | |
| 182 | - | if v := os.Getenv("ZOT_HOME"); v != "" { | |
| 181 | + | func ZutHome() string { | |
| 182 | + | if v := os.Getenv("ZUT_HOME"); v != "" { | |
| 183 | 183 | return v | |
| 184 | 184 | } | |
| 185 | 185 | if v := os.Getenv("XDG_STATE_HOME"); v != "" { | |
| 186 | - | return filepath.Join(v, "zot") | |
| 186 | + | return filepath.Join(v, "zut") | |
| 187 | 187 | } | |
| 188 | 188 | switch runtime.GOOS { | |
| 189 | 189 | case "darwin": | |
| 190 | 190 | if home, err := os.UserHomeDir(); err == nil { | |
| 191 | - | return filepath.Join(home, "Library", "Application Support", "zot") | |
| 191 | + | return filepath.Join(home, "Library", "Application Support", "zut") | |
| 192 | 192 | } | |
| 193 | 193 | case "windows": | |
| 194 | 194 | if v := os.Getenv("LOCALAPPDATA"); v != "" { | |
| 195 | - | return filepath.Join(v, "zot") | |
| 195 | + | return filepath.Join(v, "zut") | |
| 196 | 196 | } | |
| 197 | 197 | } | |
| 198 | 198 | if home, err := os.UserHomeDir(); err == nil { | |
| 199 | - | return filepath.Join(home, ".local", "state", "zot") | |
| 199 | + | return filepath.Join(home, ".local", "state", "zut") | |
| 200 | 200 | } | |
| 201 | - | return ".zot" | |
| 201 | + | return ".zut" | |
| 202 | 202 | } | |
| 203 | 203 | ||
| 204 | 204 | // ConfigPath returns the path to config.json. | |
| 205 | - | func ConfigPath() string { return filepath.Join(ZotHome(), "config.json") } | |
| 205 | + | func ConfigPath() string { return filepath.Join(ZutHome(), "config.json") } | |
| 206 | 206 | ||
| 207 | 207 | // FlatToolRender reports whether tool calls should render flat (no | |
| 208 | - | // bordered panel). The ZOT_FLAT_TOOLS env var takes precedence over | |
| 208 | + | // bordered panel). The ZUT_FLAT_TOOLS env var takes precedence over | |
| 209 | 209 | // the config when set: "1"/"true"/"yes"/"on" force flat, "0"/"false"/ | |
| 210 | 210 | // "no"/"off" force box. Otherwise the config's tool_render is | |
| 211 | 211 | // consulted; "flat" is flat, anything else (including empty) is box. | |
| 212 | 212 | func (c Config) FlatToolRender() bool { | |
| 213 | - | if v := strings.TrimSpace(strings.ToLower(os.Getenv("ZOT_FLAT_TOOLS"))); v != "" { | |
| 213 | + | if v := strings.TrimSpace(strings.ToLower(os.Getenv("ZUT_FLAT_TOOLS"))); v != "" { | |
| 214 | 214 | switch v { | |
| 215 | 215 | case "1", "true", "yes", "on", "flat": | |
| 216 | 216 | return true | |
| @@ -223,12 +223,12 @@ func (c Config) FlatToolRender() bool { | |||
| 223 | 223 | ||
| 224 | 224 | // CompactUserInput reports whether sent user messages should render as | |
| 225 | 225 | // a single quiet gutter line instead of a padded, background-tinted | |
| 226 | - | // bubble. The ZOT_COMPACT_INPUT env var takes precedence over the | |
| 226 | + | // bubble. The ZUT_COMPACT_INPUT env var takes precedence over the | |
| 227 | 227 | // config when set: "1"/"true"/"yes"/"on" force compact, "0"/"false"/ | |
| 228 | 228 | // "no"/"off" force the bubble. Otherwise the config's compact_input | |
| 229 | 229 | // is consulted (nil/false means the bubble). | |
| 230 | 230 | func (c Config) CompactUserInput() bool { | |
| 231 | - | if v := strings.TrimSpace(strings.ToLower(os.Getenv("ZOT_COMPACT_INPUT"))); v != "" { | |
| 231 | + | if v := strings.TrimSpace(strings.ToLower(os.Getenv("ZUT_COMPACT_INPUT"))); v != "" { | |
| 232 | 232 | switch v { | |
| 233 | 233 | case "1", "true", "yes", "on", "compact": | |
| 234 | 234 | return true | |
| @@ -269,19 +269,19 @@ func (c Config) LSPDiagnosticsOnEditEnabled(subagent bool) bool { | |||
| 269 | 269 | } | |
| 270 | 270 | ||
| 271 | 271 | // AuthPath returns the path to auth.json. | |
| 272 | - | func AuthPath() string { return filepath.Join(ZotHome(), "auth.json") } | |
| 272 | + | func AuthPath() string { return filepath.Join(ZutHome(), "auth.json") } | |
| 273 | 273 | ||
| 274 | 274 | // KimiCLIFallbackDisabledPath returns a sentinel that disables falling | |
| 275 | - | // back to the official Kimi Code CLI token after `zot /logout kimi`. | |
| 275 | + | // back to the official Kimi Code CLI token after `zut /logout kimi`. | |
| 276 | 276 | func KimiCLIFallbackDisabledPath() string { | |
| 277 | - | return filepath.Join(ZotHome(), "kimi-cli-fallback-disabled") | |
| 277 | + | return filepath.Join(ZutHome(), "kimi-cli-fallback-disabled") | |
| 278 | 278 | } | |
| 279 | 279 | ||
| 280 | 280 | // SessionsPath returns the directory holding session files. | |
| 281 | - | func SessionsPath() string { return filepath.Join(ZotHome(), "sessions") } | |
| 281 | + | func SessionsPath() string { return filepath.Join(ZutHome(), "sessions") } | |
| 282 | 282 | ||
| 283 | 283 | // LogsPath returns the directory holding log files. | |
| 284 | - | func LogsPath() string { return filepath.Join(ZotHome(), "logs") } | |
| 284 | + | func LogsPath() string { return filepath.Join(ZutHome(), "logs") } | |
| 285 | 285 | ||
| 286 | 286 | // LoadConfig reads the config file, returning defaults if missing. | |
| 287 | 287 | func LoadConfig() (Config, error) { | |
| @@ -301,7 +301,7 @@ func LoadConfig() (Config, error) { | |||
| 301 | 301 | ||
| 302 | 302 | // SaveConfig writes the config file, creating parent dirs. | |
| 303 | 303 | func SaveConfig(c Config) error { | |
| 304 | - | if err := os.MkdirAll(ZotHome(), 0o755); err != nil { | |
| 304 | + | if err := os.MkdirAll(ZutHome(), 0o755); err != nil { | |
| 305 | 305 | return err | |
| 306 | 306 | } | |
| 307 | 307 | b, err := json.MarshalIndent(c, "", " ") | |
M packages/agent/config_command_test.go +4 -4
| @@ -9,11 +9,11 @@ import ( | |||
| 9 | 9 | "strings" | |
| 10 | 10 | "testing" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/provider/auth" | |
| 12 | + | "github.com/bnema/zut/packages/provider/auth" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | func TestAgentAPIKeyCommandHelperProcess(t *testing.T) { | |
| 16 | - | if os.Getenv("ZOT_AGENT_API_KEY_COMMAND_HELPER") != "1" { | |
| 16 | + | if os.Getenv("ZUT_AGENT_API_KEY_COMMAND_HELPER") != "1" { | |
| 17 | 17 | return | |
| 18 | 18 | } | |
| 19 | 19 | args := os.Args | |
| @@ -32,9 +32,9 @@ func TestAgentAPIKeyCommandHelperProcess(t *testing.T) { | |||
| 32 | 32 | ||
| 33 | 33 | func TestCommandCredentialIsLazy(t *testing.T) { | |
| 34 | 34 | home := t.TempDir() | |
| 35 | - | t.Setenv("ZOT_HOME", home) | |
| 35 | + | t.Setenv("ZUT_HOME", home) | |
| 36 | 36 | t.Setenv("LAZY_PROVIDER_API_KEY", "") | |
| 37 | - | t.Setenv("ZOT_AGENT_API_KEY_COMMAND_HELPER", "1") | |
| 37 | + | t.Setenv("ZUT_AGENT_API_KEY_COMMAND_HELPER", "1") | |
| 38 | 38 | marker := filepath.Join(home, "command-ran") | |
| 39 | 39 | credentials := auth.Credentials{AdditionalAPIKeyCreds: map[string]auth.ProviderCreds{ | |
| 40 | 40 | "lazy-provider": { | |
M packages/agent/config_gcp_test.go +6 -6
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | "path/filepath" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | providerauth "github.com/patriceckhart/zot/packages/provider/auth" | |
| 8 | + | providerauth "github.com/bnema/zut/packages/provider/auth" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | func isolateGoogleVertexADC(t *testing.T) string { | |
| @@ -24,7 +24,7 @@ func isolateGoogleVertexADC(t *testing.T) string { | |||
| 24 | 24 | // takes top priority for the google-vertex provider and is returned with | |
| 25 | 25 | // method "apikey". | |
| 26 | 26 | func TestResolveCredentialGoogleVertexAPIKey(t *testing.T) { | |
| 27 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 27 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 28 | 28 | isolateGoogleVertexADC(t) | |
| 29 | 29 | t.Setenv("GOOGLE_CLOUD_API_KEY", "gcp-key-123") | |
| 30 | 30 | t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "") | |
| @@ -46,7 +46,7 @@ func TestResolveCredentialGoogleVertexAPIKey(t *testing.T) { | |||
| 46 | 46 | // resolution returns the "<adc>" sentinel with method "apikey", matching | |
| 47 | 47 | // the real NewVertex client's ADC-based auth flow. | |
| 48 | 48 | func TestResolveCredentialGoogleVertexApplicationCredentialsEnv(t *testing.T) { | |
| 49 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 49 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 50 | 50 | isolateGoogleVertexADC(t) | |
| 51 | 51 | t.Setenv("GOOGLE_CLOUD_API_KEY", "") | |
| 52 | 52 | t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/service-account.json") | |
| @@ -67,7 +67,7 @@ func TestResolveCredentialGoogleVertexApplicationCredentialsEnv(t *testing.T) { | |||
| 67 | 67 | // env vars, the presence of the platform-specific default gcloud ADC file is | |
| 68 | 68 | // detected and resolves to the "<adc>" sentinel. | |
| 69 | 69 | func TestResolveCredentialGoogleVertexDefaultADCFile(t *testing.T) { | |
| 70 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 70 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 71 | 71 | adcPath := isolateGoogleVertexADC(t) | |
| 72 | 72 | t.Setenv("GOOGLE_CLOUD_API_KEY", "") | |
| 73 | 73 | t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "") | |
| @@ -95,7 +95,7 @@ func TestResolveCredentialGoogleVertexDefaultADCFile(t *testing.T) { | |||
| 95 | 95 | // wins over GOOGLE_APPLICATION_CREDENTIALS and the default ADC file when | |
| 96 | 96 | // multiple credential sources are present simultaneously. | |
| 97 | 97 | func TestResolveCredentialGoogleVertexPrecedence(t *testing.T) { | |
| 98 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 98 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 99 | 99 | adcPath := isolateGoogleVertexADC(t) | |
| 100 | 100 | t.Setenv("GOOGLE_CLOUD_API_KEY", "explicit-key") | |
| 101 | 101 | t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/sa.json") | |
| @@ -124,7 +124,7 @@ func TestResolveCredentialGoogleVertexPrecedence(t *testing.T) { | |||
| 124 | 124 | // auth.json entry), resolution fails with an error rather than silently | |
| 125 | 125 | // returning an empty credential. | |
| 126 | 126 | func TestResolveCredentialGoogleVertexNoCredentials(t *testing.T) { | |
| 127 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 127 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 128 | 128 | isolateGoogleVertexADC(t) | |
| 129 | 129 | t.Setenv("GOOGLE_CLOUD_API_KEY", "") | |
| 130 | 130 | t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "") | |
M packages/agent/config_home_test.go +8 -8
| @@ -5,20 +5,20 @@ import ( | |||
| 5 | 5 | "testing" | |
| 6 | 6 | ) | |
| 7 | 7 | ||
| 8 | - | func TestZotHomePrefersExplicitHomeOverXDGStateHome(t *testing.T) { | |
| 9 | - | t.Setenv("ZOT_HOME", filepath.Join("explicit", "zot")) | |
| 8 | + | func TestZutHomePrefersExplicitHomeOverXDGStateHome(t *testing.T) { | |
| 9 | + | t.Setenv("ZUT_HOME", filepath.Join("explicit", "zut")) | |
| 10 | 10 | t.Setenv("XDG_STATE_HOME", filepath.Join("xdg", "state")) | |
| 11 | 11 | ||
| 12 | - | if got, want := ZotHome(), filepath.Join("explicit", "zot"); got != want { | |
| 13 | - | t.Fatalf("ZotHome() = %q, want %q", got, want) | |
| 12 | + | if got, want := ZutHome(), filepath.Join("explicit", "zut"); got != want { | |
| 13 | + | t.Fatalf("ZutHome() = %q, want %q", got, want) | |
| 14 | 14 | } | |
| 15 | 15 | } | |
| 16 | 16 | ||
| 17 | - | func TestZotHomeUsesXDGStateHome(t *testing.T) { | |
| 18 | - | t.Setenv("ZOT_HOME", "") | |
| 17 | + | func TestZutHomeUsesXDGStateHome(t *testing.T) { | |
| 18 | + | t.Setenv("ZUT_HOME", "") | |
| 19 | 19 | t.Setenv("XDG_STATE_HOME", filepath.Join("xdg", "state")) | |
| 20 | 20 | ||
| 21 | - | if got, want := ZotHome(), filepath.Join("xdg", "state", "zot"); got != want { | |
| 22 | - | t.Fatalf("ZotHome() = %q, want %q", got, want) | |
| 21 | + | if got, want := ZutHome(), filepath.Join("xdg", "state", "zut"); got != want { | |
| 22 | + | t.Fatalf("ZutHome() = %q, want %q", got, want) | |
| 23 | 23 | } | |
| 24 | 24 | } | |
M packages/agent/confirmation_events.go +2 -2
| @@ -1,8 +1,8 @@ | |||
| 1 | 1 | package agent | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 5 | - | "github.com/patriceckhart/zot/packages/core" | |
| 4 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 5 | + | "github.com/bnema/zut/packages/core" | |
| 6 | 6 | ) | |
| 7 | 7 | ||
| 8 | 8 | // confirmationEventConfirmer decorates an interactive confirmer with an | |
M packages/agent/confirmation_events_test.go +2 -2
| @@ -4,8 +4,8 @@ import ( | |||
| 4 | 4 | "reflect" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 8 | - | "github.com/patriceckhart/zot/packages/core" | |
| 7 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 8 | + | "github.com/bnema/zut/packages/core" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | type rememberingDetailedConfirmer struct { | |
M packages/agent/ext/ext.go +33 -29
| @@ -1,6 +1,6 @@ | |||
| 1 | - | // Package ext is the Go SDK for writing zot extensions. | |
| 1 | + | // Package ext is the Go SDK for writing zut extensions. | |
| 2 | 2 | // | |
| 3 | - | // An extension is a subprocess that talks to zot over its stdin/stdout | |
| 3 | + | // An extension is a subprocess that talks to zut over its stdin/stdout | |
| 4 | 4 | // in newline-delimited JSON. This package wraps the wire format so | |
| 5 | 5 | // extension authors can write straightforward Go without reimplementing | |
| 6 | 6 | // the protocol. | |
| @@ -9,7 +9,7 @@ | |||
| 9 | 9 | // | |
| 10 | 10 | // package main | |
| 11 | 11 | // | |
| 12 | - | // import "github.com/patriceckhart/zot/packages/agent/ext" | |
| 12 | + | // import "github.com/bnema/zut/packages/agent/ext" | |
| 13 | 13 | // | |
| 14 | 14 | // func main() { | |
| 15 | 15 | // ext := ext.New("hello", "1.0.0") | |
| @@ -20,10 +20,10 @@ | |||
| 20 | 20 | // } | |
| 21 | 21 | // | |
| 22 | 22 | // Build it, drop the binary + an extension.json next to it under | |
| 23 | - | // `$ZOT_HOME/extensions/hello/`, and zot picks it up on next launch. | |
| 23 | + | // `$ZUT_HOME/extensions/hello/`, and zut picks it up on next launch. | |
| 24 | 24 | // | |
| 25 | 25 | // Use OnHello when registration or configuration depends on host metadata | |
| 26 | - | // such as HostInfo.CWD, Provider, Model, ZotVersion, ExtensionDir, or DataDir. | |
| 26 | + | // such as HostInfo.CWD, Provider, Model, ZutVersion, ExtensionDir, or DataDir. | |
| 27 | 27 | // Run sends hello, waits for hello_ack, runs OnHello, announces registrations, | |
| 28 | 28 | // then sends ready. | |
| 29 | 29 | // | |
| @@ -40,7 +40,7 @@ import ( | |||
| 40 | 40 | "os" | |
| 41 | 41 | "sync" | |
| 42 | 42 | ||
| 43 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 43 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 44 | 44 | ) | |
| 45 | 45 | ||
| 46 | 46 | func base64Encode(b []byte) string { return base64.StdEncoding.EncodeToString(b) } | |
| @@ -48,13 +48,13 @@ func base64Encode(b []byte) string { return base64.StdEncoding.EncodeToString(b) | |||
| 48 | 48 | // CommandHandler is invoked when the user runs the extension's | |
| 49 | 49 | // registered slash command. args is everything the user typed after | |
| 50 | 50 | // the command name (already trimmed). Return a Response describing | |
| 51 | - | // what zot should do next. | |
| 51 | + | // what zut should do next. | |
| 52 | 52 | type CommandHandler func(args string) Response | |
| 53 | 53 | ||
| 54 | 54 | // ToolHandler is invoked when the LLM calls a tool the extension | |
| 55 | 55 | // registered. args is the raw JSON object the model produced; the | |
| 56 | 56 | // handler is responsible for parsing/validating it. Return a | |
| 57 | - | // ToolResult describing what zot should send back to the model. | |
| 57 | + | // ToolResult describing what zut should send back to the model. | |
| 58 | 58 | type ToolHandler func(args json.RawMessage) ToolResult | |
| 59 | 59 | ||
| 60 | 60 | // EventHandler is called for each lifecycle event the extension | |
| @@ -63,7 +63,7 @@ type ToolHandler func(args json.RawMessage) ToolResult | |||
| 63 | 63 | // worker. | |
| 64 | 64 | type EventHandler func(ev Event) | |
| 65 | 65 | ||
| 66 | - | // Event is a lifecycle notification from zot. The fields populated | |
| 66 | + | // Event is a lifecycle notification from zut. The fields populated | |
| 67 | 67 | // depend on Name (the host's event_name string): | |
| 68 | 68 | // | |
| 69 | 69 | // session_start : Session | |
| @@ -221,7 +221,7 @@ func TextErrorResult(s string) ToolResult { | |||
| 221 | 221 | return ToolResult{Content: []ToolContent{Text(s)}, IsError: true} | |
| 222 | 222 | } | |
| 223 | 223 | ||
| 224 | - | // Response tells zot how to react to a command invocation. Construct | |
| 224 | + | // Response tells zut how to react to a command invocation. Construct | |
| 225 | 225 | // one with Prompt(), Insert(), Display(), or Noop(). | |
| 226 | 226 | type Panel struct { | |
| 227 | 227 | ID string | |
| @@ -254,7 +254,7 @@ func Insert(text string) Response { return Response{Action: "insert", Insert: te | |||
| 254 | 254 | func Display(text string) Response { return Response{Action: "display", Display: text} } | |
| 255 | 255 | ||
| 256 | 256 | // OpenPanel returns a Response that opens an interactive extension-owned | |
| 257 | - | // panel inside zot. | |
| 257 | + | // panel inside zut. | |
| 258 | 258 | func OpenPanel(id, title string, lines []string, footer string) Response { | |
| 259 | 259 | return Response{Action: "open_panel", OpenPanel: &Panel{ID: id, Title: title, Lines: lines, Footer: footer}} | |
| 260 | 260 | } | |
| @@ -269,7 +269,7 @@ func Errorf(format string, args ...any) Response { | |||
| 269 | 269 | return Response{Action: "noop", Error: fmt.Sprintf(format, args...)} | |
| 270 | 270 | } | |
| 271 | 271 | ||
| 272 | - | // Extension is one zot extension. Construct with New, register | |
| 272 | + | // Extension is one zut extension. Construct with New, register | |
| 273 | 273 | // commands, then call Run. | |
| 274 | 274 | type Extension struct { | |
| 275 | 275 | name string | |
| @@ -315,11 +315,11 @@ type toolDef struct { | |||
| 315 | 315 | deferred bool | |
| 316 | 316 | } | |
| 317 | 317 | ||
| 318 | - | // HostInfo is what the host (zot) tells us in HelloAck. Useful for | |
| 318 | + | // HostInfo is what the host (zut) tells us in HelloAck. Useful for | |
| 319 | 319 | // extensions that want to behave differently per provider. | |
| 320 | 320 | type HostInfo struct { | |
| 321 | 321 | ProtocolVersion int | |
| 322 | - | ZotVersion string | |
| 322 | + | ZutVersion string | |
| 323 | 323 | Provider string | |
| 324 | 324 | Model string | |
| 325 | 325 | CWD string | |
| @@ -350,9 +350,9 @@ func New(name, version string) *Extension { | |||
| 350 | 350 | // Returns the zero value if Run hasn't started yet. | |
| 351 | 351 | func (e *Extension) Host() HostInfo { return e.host } | |
| 352 | 352 | ||
| 353 | - | // OnHello registers a callback that runs after zot acknowledges the | |
| 353 | + | // OnHello registers a callback that runs after zut acknowledges the | |
| 354 | 354 | // hello frame and before commands/tools are announced. Use it when | |
| 355 | - | // registrations depend on host metadata such as CWD or ZotHome-derived | |
| 355 | + | // registrations depend on host metadata such as CWD or ZutHome-derived | |
| 356 | 356 | // paths. The callback may call Command, Tool, On, and interceptor | |
| 357 | 357 | // registration methods. | |
| 358 | 358 | func (e *Extension) OnHello(fn func(HostInfo)) { | |
| @@ -361,8 +361,8 @@ func (e *Extension) OnHello(fn func(HostInfo)) { | |||
| 361 | 361 | e.mu.Unlock() | |
| 362 | 362 | } | |
| 363 | 363 | ||
| 364 | - | // Logf writes a line to the extension's stderr, which zot captures to | |
| 365 | - | // $ZOT_HOME/logs/ext-<name>.log. Use this for debug output: anything | |
| 364 | + | // Logf writes a line to the extension's stderr, which zut captures to | |
| 365 | + | // $ZUT_HOME/logs/ext-<name>.log. Use this for debug output: anything | |
| 366 | 366 | // you print to stdout would corrupt the JSON wire protocol. | |
| 367 | 367 | func (e *Extension) Logf(format string, args ...any) { | |
| 368 | 368 | fmt.Fprintf(e.stderr, "["+e.name+"] "+format+"\n", args...) | |
| @@ -396,13 +396,13 @@ func (e *Extension) RenderPanel(panelID, title string, lines []string, footer st | |||
| 396 | 396 | _ = e.send(extproto.PanelRenderFromExt{Type: "panel_render", PanelID: panelID, Title: title, Lines: lines, Footer: footer}) | |
| 397 | 397 | } | |
| 398 | 398 | ||
| 399 | - | // ClosePanel tells zot to close panelID. | |
| 399 | + | // ClosePanel tells zut to close panelID. | |
| 400 | 400 | func (e *Extension) ClosePanel(panelID string) { | |
| 401 | 401 | _ = e.send(extproto.PanelCloseFromExt{Type: "panel_close", PanelID: panelID}) | |
| 402 | 402 | } | |
| 403 | 403 | ||
| 404 | 404 | // Command registers a slash-command handler. Call this BEFORE Run(). | |
| 405 | - | // Once Run is going, when the user runs /name in zot, fn is invoked | |
| 405 | + | // Once Run is going, when the user runs /name in zut, fn is invoked | |
| 406 | 406 | // with the remaining args. | |
| 407 | 407 | // | |
| 408 | 408 | // Naming conflicts with built-in commands (e.g. /help) are silently | |
| @@ -417,7 +417,7 @@ func (e *Extension) Command(name, description string, fn CommandHandler) { | |||
| 417 | 417 | ||
| 418 | 418 | // Tool registers an LLM-callable tool. schema is a JSON Schema | |
| 419 | 419 | // object describing the tool's args (the same shape Anthropic / | |
| 420 | - | // OpenAI accept). Call this BEFORE Run(); zot folds extension tools | |
| 420 | + | // OpenAI accept). Call this BEFORE Run(); zut folds extension tools | |
| 421 | 421 | // into the agent's registry once the extension's ready frame fires. | |
| 422 | 422 | // | |
| 423 | 423 | // Naming conflicts with built-in tools (read, write, edit, bash, lsp, | |
| @@ -501,7 +501,7 @@ func (e *Extension) InterceptAssistantMessage(fn AssistantMessageHandler) { | |||
| 501 | 501 | e.mu.Unlock() | |
| 502 | 502 | } | |
| 503 | 503 | ||
| 504 | - | // Notify pushes an info-level status note into zot's chat without | |
| 504 | + | // Notify pushes an info-level status note into zut's chat without | |
| 505 | 505 | // requiring a slash command from the user. | |
| 506 | 506 | func (e *Extension) Notify(level, message string) { | |
| 507 | 507 | _ = e.send(extproto.NotifyFromExt{ | |
| @@ -549,20 +549,21 @@ func (e *Extension) Alert(alert AlertRequest) { | |||
| 549 | 549 | }) | |
| 550 | 550 | } | |
| 551 | 551 | ||
| 552 | - | // Run starts the protocol loop. Blocks until stdin closes (zot has | |
| 552 | + | // Run starts the protocol loop. Blocks until stdin closes (zut has | |
| 553 | 553 | // shut us down). Returns the first fatal error, or nil on clean exit. | |
| 554 | 554 | func (e *Extension) Run() error { | |
| 555 | 555 | scanner := bufio.NewScanner(e.in) | |
| 556 | 556 | scanner.Buffer(make([]byte, 0, 64*1024), 4*1024*1024) | |
| 557 | 557 | ||
| 558 | - | // Send hello first. zot answers with hello_ack immediately; read it | |
| 558 | + | // Send hello first. zut answers with hello_ack immediately; read it | |
| 559 | 559 | // synchronously so extensions can use HostInfo while registering | |
| 560 | 560 | // commands/tools before the ready frame. | |
| 561 | 561 | if err := e.send(extproto.HelloFromExt{ | |
| 562 | - | Type: "hello", | |
| 563 | - | Name: e.name, | |
| 564 | - | Version: e.version, | |
| 565 | - | Capabilities: e.caps, | |
| 562 | + | Type: "hello", | |
| 563 | + | ProtocolVersion: extproto.ProtocolVersion, | |
| 564 | + | Name: e.name, | |
| 565 | + | Version: e.version, | |
| 566 | + | Capabilities: e.caps, | |
| 566 | 567 | }); err != nil { | |
| 567 | 568 | return err | |
| 568 | 569 | } | |
| @@ -576,9 +577,12 @@ func (e *Extension) Run() error { | |||
| 576 | 577 | if ack.Type != "hello_ack" { | |
| 577 | 578 | return fmt.Errorf("first host frame must be hello_ack (got %q)", ack.Type) | |
| 578 | 579 | } | |
| 580 | + | if ack.ProtocolVersion != extproto.ProtocolVersion { | |
| 581 | + | return fmt.Errorf("unsupported host protocol version %d (want %d)", ack.ProtocolVersion, extproto.ProtocolVersion) | |
| 582 | + | } | |
| 579 | 583 | e.host = HostInfo{ | |
| 580 | 584 | ProtocolVersion: ack.ProtocolVersion, | |
| 581 | - | ZotVersion: ack.ZotVersion, | |
| 585 | + | ZutVersion: ack.ZutVersion, | |
| 582 | 586 | Provider: ack.Provider, | |
| 583 | 587 | Model: ack.Model, | |
| 584 | 588 | CWD: ack.CWD, | |
M packages/agent/ext/ext_test.go +31 -3
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | 9 | "testing" | |
| 10 | 10 | "time" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 12 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | // ---------- test harness ---------- | |
| @@ -140,10 +140,17 @@ func (h *extHarness) handshake(t *testing.T) { | |||
| 140 | 140 | if f.hdr.Type != "hello" { | |
| 141 | 141 | t.Fatalf("expected hello, got %q", f.hdr.Type) | |
| 142 | 142 | } | |
| 143 | + | var hello extproto.HelloFromExt | |
| 144 | + | if err := json.Unmarshal(f.raw, &hello); err != nil { | |
| 145 | + | t.Fatalf("unmarshal hello: %v", err) | |
| 146 | + | } | |
| 147 | + | if hello.ProtocolVersion != extproto.ProtocolVersion { | |
| 148 | + | t.Fatalf("hello protocol version = %d, want %d", hello.ProtocolVersion, extproto.ProtocolVersion) | |
| 149 | + | } | |
| 143 | 150 | h.sendToExt(t, extproto.HelloAckFromHost{ | |
| 144 | 151 | Type: "hello_ack", | |
| 145 | 152 | ProtocolVersion: extproto.ProtocolVersion, | |
| 146 | - | ZotVersion: "0.0.0-test", | |
| 153 | + | ZutVersion: "0.0.0-test", | |
| 147 | 154 | Provider: "anthropic", | |
| 148 | 155 | Model: "claude-test", | |
| 149 | 156 | }) | |
| @@ -157,6 +164,27 @@ func (h *extHarness) handshake(t *testing.T) { | |||
| 157 | 164 | ||
| 158 | 165 | // ---------- tests ---------- | |
| 159 | 166 | ||
| 167 | + | func TestRunRejectsUnsupportedHostProtocolVersion(t *testing.T) { | |
| 168 | + | h := newHarness("versioned-ext") | |
| 169 | + | runDone := make(chan error, 1) | |
| 170 | + | go func() { runDone <- h.ext.Run() }() | |
| 171 | + | t.Cleanup(func() { _ = h.hostW.Close() }) | |
| 172 | + | ||
| 173 | + | if frame := h.next(t); frame.hdr.Type != "hello" { | |
| 174 | + | t.Fatalf("expected hello, got %q", frame.hdr.Type) | |
| 175 | + | } | |
| 176 | + | h.sendToExt(t, extproto.HelloAckFromHost{Type: "hello_ack", ProtocolVersion: extproto.ProtocolVersion - 1}) | |
| 177 | + | ||
| 178 | + | select { | |
| 179 | + | case err := <-runDone: | |
| 180 | + | if err == nil || !strings.Contains(err.Error(), "unsupported host protocol version") { | |
| 181 | + | t.Fatalf("Run error = %v", err) | |
| 182 | + | } | |
| 183 | + | case <-time.After(2 * time.Second): | |
| 184 | + | t.Fatal("Run did not reject an unsupported host protocol version") | |
| 185 | + | } | |
| 186 | + | } | |
| 187 | + | ||
| 160 | 188 | // TestOnHelloCanRegisterUsingHostInfo checks that extensions can use | |
| 161 | 189 | // host metadata before announcing their initial registrations. | |
| 162 | 190 | func TestOnHelloCanRegisterUsingHostInfo(t *testing.T) { | |
| @@ -177,7 +205,7 @@ func TestOnHelloCanRegisterUsingHostInfo(t *testing.T) { | |||
| 177 | 205 | h.sendToExt(t, extproto.HelloAckFromHost{ | |
| 178 | 206 | Type: "hello_ack", | |
| 179 | 207 | ProtocolVersion: extproto.ProtocolVersion, | |
| 180 | - | ZotVersion: "0.0.0-test", | |
| 208 | + | ZutVersion: "0.0.0-test", | |
| 181 | 209 | Provider: "anthropic", | |
| 182 | 210 | Model: "claude-test", | |
| 183 | 211 | CWD: "/tmp/project", | |
M packages/agent/extcmd.go +38 -38
| @@ -14,12 +14,12 @@ import ( | |||
| 14 | 14 | "strings" | |
| 15 | 15 | "time" | |
| 16 | 16 | ||
| 17 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 18 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 19 | - | "github.com/patriceckhart/zot/packages/ignore" | |
| 17 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 18 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 19 | + | "github.com/bnema/zut/packages/ignore" | |
| 20 | 20 | ) | |
| 21 | 21 | ||
| 22 | - | // runExtCommand dispatches `zot ext ...` subcommands. Returns | |
| 22 | + | // runExtCommand dispatches `zut ext ...` subcommands. Returns | |
| 23 | 23 | // (handled=true, err) if rawArgs starts with "ext"; otherwise | |
| 24 | 24 | // (handled=false, nil) so the main router falls through to the | |
| 25 | 25 | // regular flag parser. | |
| @@ -56,22 +56,22 @@ func runExtCommand(ctx context.Context, rawArgs []string, version string) (handl | |||
| 56 | 56 | } | |
| 57 | 57 | ||
| 58 | 58 | func printExtHelp() { | |
| 59 | - | fmt.Fprintln(os.Stderr, `zot ext — manage extensions | |
| 59 | + | fmt.Fprintln(os.Stderr, `zut ext — manage extensions | |
| 60 | 60 | ||
| 61 | 61 | usage: | |
| 62 | - | zot ext list list installed extensions and their state | |
| 63 | - | zot ext doctor diagnose installed extensions | |
| 64 | - | zot ext logs <name> [-f] cat / tail an extension's stderr log | |
| 65 | - | zot ext enable <name> re-enable a disabled extension | |
| 66 | - | zot ext disable <name> disable without removing | |
| 67 | - | zot ext remove <name> delete an extension directory | |
| 68 | - | zot ext install [--build=go] <path|git-url> | |
| 62 | + | zut ext list list installed extensions and their state | |
| 63 | + | zut ext doctor diagnose installed extensions | |
| 64 | + | zut ext logs <name> [-f] cat / tail an extension's stderr log | |
| 65 | + | zut ext enable <name> re-enable a disabled extension | |
| 66 | + | zut ext disable <name> disable without removing | |
| 67 | + | zut ext remove <name> delete an extension directory | |
| 68 | + | zut ext install [--build=go] <path|git-url> | |
| 69 | 69 | copy / clone and validate an extension | |
| 70 | 70 | --build=go explicitly builds a local Go extension | |
| 71 | 71 | ||
| 72 | 72 | extensions live under: | |
| 73 | - | $ZOT_HOME/extensions/<name>/extension.json (global) | |
| 74 | - | ./.zot/extensions/<name>/extension.json (project-local)`) | |
| 73 | + | $ZUT_HOME/extensions/<name>/extension.json (global) | |
| 74 | + | ./.zut/extensions/<name>/extension.json (project-local)`) | |
| 75 | 75 | } | |
| 76 | 76 | ||
| 77 | 77 | // extList walks both the global and project-local extension dirs and | |
| @@ -122,7 +122,7 @@ func extList() error { | |||
| 122 | 122 | } | |
| 123 | 123 | if len(rows) == 0 { | |
| 124 | 124 | fmt.Fprintln(os.Stderr, "no extensions installed") | |
| 125 | - | fmt.Fprintln(os.Stderr, "see docs/extensions.md to write your own, or `zot ext install <path|url>`") | |
| 125 | + | fmt.Fprintln(os.Stderr, "see docs/extensions.md to write your own, or `zut ext install <path|url>`") | |
| 126 | 126 | return nil | |
| 127 | 127 | } | |
| 128 | 128 | fmt.Printf("%-12s %-20s %-10s %-8s %-10s %s\n", "scope", "name", "version", "enabled", "language", "dir") | |
| @@ -169,14 +169,14 @@ func extDoctor(version string) error { | |||
| 169 | 169 | rows := scanExtDoctorStatic() | |
| 170 | 170 | if len(rows) == 0 { | |
| 171 | 171 | fmt.Fprintln(os.Stdout, "no extensions installed") | |
| 172 | - | fmt.Fprintln(os.Stdout, "see docs/extensions.md to write your own, or `zot ext install <path|url>`") | |
| 172 | + | fmt.Fprintln(os.Stdout, "see docs/extensions.md to write your own, or `zut ext install <path|url>`") | |
| 173 | 173 | return nil | |
| 174 | 174 | } | |
| 175 | 175 | ||
| 176 | 176 | cwd, _ := os.Getwd() | |
| 177 | 177 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | |
| 178 | 178 | defer cancel() | |
| 179 | - | mgr := extensions.New(ZotHome(), cwd, version, "", "", extDoctorHooks{}) | |
| 179 | + | mgr := extensions.New(ZutHome(), cwd, version, "", "", extDoctorHooks{}) | |
| 180 | 180 | errs := mgr.Discover(ctx) | |
| 181 | 181 | mgr.WaitForReady(3 * time.Second) | |
| 182 | 182 | diags := mgr.Diagnostics() | |
| @@ -187,7 +187,7 @@ func extDoctor(version string) error { | |||
| 187 | 187 | diagByDir[d.Dir] = d | |
| 188 | 188 | } | |
| 189 | 189 | ||
| 190 | - | fmt.Fprintln(os.Stdout, "zot extension doctor") | |
| 190 | + | fmt.Fprintln(os.Stdout, "zut extension doctor") | |
| 191 | 191 | fmt.Fprintln(os.Stdout) | |
| 192 | 192 | for _, row := range rows { | |
| 193 | 193 | printExtDoctorRow(os.Stdout, row, diagByDir[row.Dir]) | |
| @@ -208,9 +208,9 @@ func scanExtDoctorStatic() []extDoctorStaticRow { | |||
| 208 | 208 | } | |
| 209 | 209 | var dirs []scanDir | |
| 210 | 210 | if cwd, err := os.Getwd(); err == nil { | |
| 211 | - | dirs = append(dirs, scanDir{scope: "project", dir: filepath.Join(cwd, ".zot", "extensions")}) | |
| 211 | + | dirs = append(dirs, scanDir{scope: "project", dir: filepath.Join(cwd, ".zut", "extensions")}) | |
| 212 | 212 | } | |
| 213 | - | if h := ZotHome(); h != "" { | |
| 213 | + | if h := ZutHome(); h != "" { | |
| 214 | 214 | dirs = append(dirs, scanDir{scope: "global", dir: filepath.Join(h, "extensions")}) | |
| 215 | 215 | } | |
| 216 | 216 | ||
| @@ -357,17 +357,17 @@ func printExtDoctorRow(w io.Writer, row extDoctorStaticRow, diag extensions.Exte | |||
| 357 | 357 | } | |
| 358 | 358 | ||
| 359 | 359 | func extDoctorLogPath(name string) string { | |
| 360 | - | if name == "" || ZotHome() == "" { | |
| 360 | + | if name == "" || ZutHome() == "" { | |
| 361 | 361 | return "" | |
| 362 | 362 | } | |
| 363 | - | return filepath.Join(ZotHome(), "logs", "ext-"+name+".log") | |
| 363 | + | return filepath.Join(ZutHome(), "logs", "ext-"+name+".log") | |
| 364 | 364 | } | |
| 365 | 365 | ||
| 366 | 366 | // extLogs locates the named extension's log file and either cats or | |
| 367 | 367 | // tails it (-f). | |
| 368 | 368 | func extLogs(args []string) error { | |
| 369 | 369 | if len(args) == 0 { | |
| 370 | - | return fmt.Errorf("usage: zot ext logs <name> [-f]") | |
| 370 | + | return fmt.Errorf("usage: zut ext logs <name> [-f]") | |
| 371 | 371 | } | |
| 372 | 372 | name := args[0] | |
| 373 | 373 | follow := false | |
| @@ -376,7 +376,7 @@ func extLogs(args []string) error { | |||
| 376 | 376 | follow = true | |
| 377 | 377 | } | |
| 378 | 378 | } | |
| 379 | - | logPath := filepath.Join(ZotHome(), "logs", "ext-"+name+".log") | |
| 379 | + | logPath := filepath.Join(ZutHome(), "logs", "ext-"+name+".log") | |
| 380 | 380 | if _, err := os.Stat(logPath); err != nil { | |
| 381 | 381 | return fmt.Errorf("no log for %q at %s", name, logPath) | |
| 382 | 382 | } | |
| @@ -402,7 +402,7 @@ func extToggle(args []string, enabled bool) error { | |||
| 402 | 402 | if !enabled { | |
| 403 | 403 | verb = "disable" | |
| 404 | 404 | } | |
| 405 | - | return fmt.Errorf("usage: zot ext %s <name>", verb) | |
| 405 | + | return fmt.Errorf("usage: zut ext %s <name>", verb) | |
| 406 | 406 | } | |
| 407 | 407 | name := args[0] | |
| 408 | 408 | dir, err := findExtensionDir(name) | |
| @@ -438,7 +438,7 @@ func extToggle(args []string, enabled bool) error { | |||
| 438 | 438 | // prompt (skip with --yes). | |
| 439 | 439 | func extRemove(args []string) error { | |
| 440 | 440 | if len(args) == 0 { | |
| 441 | - | return fmt.Errorf("usage: zot ext remove <name> [--yes]") | |
| 441 | + | return fmt.Errorf("usage: zut ext remove <name> [--yes]") | |
| 442 | 442 | } | |
| 443 | 443 | name := args[0] | |
| 444 | 444 | yes := false | |
| @@ -468,7 +468,7 @@ func extRemove(args []string) error { | |||
| 468 | 468 | } | |
| 469 | 469 | ||
| 470 | 470 | // extInstall copies a local directory or shallow-clones a git URL | |
| 471 | - | // into $ZOT_HOME/extensions/. It stages the install and validates the | |
| 471 | + | // into $ZUT_HOME/extensions/. It stages the install and validates the | |
| 472 | 472 | // manifest plus any extension-local executable before reporting success. | |
| 473 | 473 | // Builds are never inferred or run implicitly; --build=go is an explicit | |
| 474 | 474 | // opt-in for local Go extensions. | |
| @@ -482,7 +482,7 @@ func extInstallContext(ctx context.Context, args []string) error { | |||
| 482 | 482 | return err | |
| 483 | 483 | } | |
| 484 | 484 | src := opts.source | |
| 485 | - | dest := filepath.Join(ZotHome(), "extensions") | |
| 485 | + | dest := filepath.Join(ZutHome(), "extensions") | |
| 486 | 486 | if err := os.MkdirAll(dest, 0o755); err != nil { | |
| 487 | 487 | return err | |
| 488 | 488 | } | |
| @@ -517,7 +517,7 @@ func extInstallContext(ctx context.Context, args []string) error { | |||
| 517 | 517 | return fmt.Errorf("destination %s already exists; remove it first", out) | |
| 518 | 518 | } | |
| 519 | 519 | ||
| 520 | - | stageRoot, err := os.MkdirTemp(dest, ".zot-extension-install-") | |
| 520 | + | stageRoot, err := os.MkdirTemp(dest, ".zut-extension-install-") | |
| 521 | 521 | if err != nil { | |
| 522 | 522 | return fmt.Errorf("create install staging directory: %w", err) | |
| 523 | 523 | } | |
| @@ -548,7 +548,7 @@ type extInstallOptions struct { | |||
| 548 | 548 | ||
| 549 | 549 | func parseExtInstallArgs(args []string) (extInstallOptions, error) { | |
| 550 | 550 | if len(args) == 0 { | |
| 551 | - | return extInstallOptions{}, errors.New("usage: zot ext install [--build=go] <path|git-url>") | |
| 551 | + | return extInstallOptions{}, errors.New("usage: zut ext install [--build=go] <path|git-url>") | |
| 552 | 552 | } | |
| 553 | 553 | ||
| 554 | 554 | var opts extInstallOptions | |
| @@ -581,13 +581,13 @@ func parseExtInstallArgs(args []string) (extInstallOptions, error) { | |||
| 581 | 581 | case strings.HasPrefix(arg, "-"): | |
| 582 | 582 | return extInstallOptions{}, fmt.Errorf("unknown ext install option %q", arg) | |
| 583 | 583 | case opts.source != "": | |
| 584 | - | return extInstallOptions{}, errors.New("usage: zot ext install [--build=go] <path|git-url>") | |
| 584 | + | return extInstallOptions{}, errors.New("usage: zut ext install [--build=go] <path|git-url>") | |
| 585 | 585 | default: | |
| 586 | 586 | opts.source = arg | |
| 587 | 587 | } | |
| 588 | 588 | } | |
| 589 | 589 | if opts.source == "" { | |
| 590 | - | return extInstallOptions{}, errors.New("usage: zot ext install [--build=go] <path|git-url>") | |
| 590 | + | return extInstallOptions{}, errors.New("usage: zut ext install [--build=go] <path|git-url>") | |
| 591 | 591 | } | |
| 592 | 592 | if opts.builder != "" && opts.builder != "go" { | |
| 593 | 593 | return extInstallOptions{}, fmt.Errorf("unsupported extension builder %q (currently supported: go)", opts.builder) | |
| @@ -639,7 +639,7 @@ func extensionRelativeExecPath(dir, execName string) (string, error) { | |||
| 639 | 639 | } | |
| 640 | 640 | ||
| 641 | 641 | func installGitExtension(ctx context.Context, src, dest string) error { | |
| 642 | - | stageRoot, err := os.MkdirTemp(dest, ".zot-extension-clone-") | |
| 642 | + | stageRoot, err := os.MkdirTemp(dest, ".zut-extension-clone-") | |
| 643 | 643 | if err != nil { | |
| 644 | 644 | return fmt.Errorf("create install staging directory: %w", err) | |
| 645 | 645 | } | |
| @@ -733,14 +733,14 @@ func validateExtensionExecutable(dir string, manifest extensions.Manifest) error | |||
| 733 | 733 | path, local := extensions.ResolveExecPath(dir, manifest.Exec) | |
| 734 | 734 | if !local { | |
| 735 | 735 | // Bare commands such as "go", "node", and "npx" are resolved | |
| 736 | - | // from PATH when zot starts. They may be intentionally installed | |
| 736 | + | // from PATH when zut starts. They may be intentionally installed | |
| 737 | 737 | // before the runtime is present on the current machine. | |
| 738 | 738 | return nil | |
| 739 | 739 | } | |
| 740 | 740 | info, err := os.Stat(path) | |
| 741 | 741 | if err != nil { | |
| 742 | 742 | if os.IsNotExist(err) { | |
| 743 | - | return fmt.Errorf("extension executable %q is missing; provide it before installing. Local Go extensions can opt in with `zot ext install --build=go <path>`", manifest.Exec) | |
| 743 | + | return fmt.Errorf("extension executable %q is missing; provide it before installing. Local Go extensions can opt in with `zut ext install --build=go <path>`", manifest.Exec) | |
| 744 | 744 | } | |
| 745 | 745 | return fmt.Errorf("stat extension executable %q: %w", manifest.Exec, err) | |
| 746 | 746 | } | |
| @@ -755,11 +755,11 @@ func validateExtensionExecutable(dir string, manifest extensions.Manifest) error | |||
| 755 | 755 | ||
| 756 | 756 | func extensionDirs() map[string]string { | |
| 757 | 757 | out := map[string]string{} | |
| 758 | - | if h := ZotHome(); h != "" { | |
| 758 | + | if h := ZutHome(); h != "" { | |
| 759 | 759 | out["global"] = filepath.Join(h, "extensions") | |
| 760 | 760 | } | |
| 761 | 761 | if cwd, err := os.Getwd(); err == nil { | |
| 762 | - | out["project"] = filepath.Join(cwd, ".zot", "extensions") | |
| 762 | + | out["project"] = filepath.Join(cwd, ".zut", "extensions") | |
| 763 | 763 | } | |
| 764 | 764 | return out | |
| 765 | 765 | } | |
| @@ -782,7 +782,7 @@ func dashIfEmpty(s string) string { | |||
| 782 | 782 | } | |
| 783 | 783 | ||
| 784 | 784 | // copyDir does a recursive copy of src to dst preserving file mode | |
| 785 | - | // bits. Used by `zot ext install <local-path>`. | |
| 785 | + | // bits. Used by `zut ext install <local-path>`. | |
| 786 | 786 | // | |
| 787 | 787 | // Entries matched by the source's root .gitignore are skipped, and | |
| 788 | 788 | // .git itself is always skipped. This keeps non-portable, regeneratable | |
M packages/agent/extcmd_test.go +21 -21
| @@ -9,15 +9,15 @@ import ( | |||
| 9 | 9 | "strings" | |
| 10 | 10 | "testing" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 12 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | - | // TestExtInstallDotSource verifies that `zot ext install .` resolves the | |
| 15 | + | // TestExtInstallDotSource verifies that `zut ext install .` resolves the | |
| 16 | 16 | // source directory correctly instead of collapsing it to the extensions/ | |
| 17 | 17 | // parent directory. | |
| 18 | 18 | func TestExtInstallDotSource(t *testing.T) { | |
| 19 | 19 | home := t.TempDir() | |
| 20 | - | t.Setenv("ZOT_HOME", home) | |
| 20 | + | t.Setenv("ZUT_HOME", home) | |
| 21 | 21 | ||
| 22 | 22 | // Pre-create extensions/ to mimic a normal first run. | |
| 23 | 23 | if err := os.MkdirAll(filepath.Join(home, "extensions"), 0o755); err != nil { | |
| @@ -56,7 +56,7 @@ func TestExtInstallDotSource(t *testing.T) { | |||
| 56 | 56 | // successfully when its manifest uses a PATH-resolved executable. | |
| 57 | 57 | func TestExtInstallNamedDir(t *testing.T) { | |
| 58 | 58 | home := t.TempDir() | |
| 59 | - | t.Setenv("ZOT_HOME", home) | |
| 59 | + | t.Setenv("ZUT_HOME", home) | |
| 60 | 60 | ||
| 61 | 61 | src := filepath.Join(t.TempDir(), "myext") | |
| 62 | 62 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| @@ -76,7 +76,7 @@ func TestExtInstallNamedDir(t *testing.T) { | |||
| 76 | 76 | ||
| 77 | 77 | func TestExtInstallThemeOnly(t *testing.T) { | |
| 78 | 78 | home := t.TempDir() | |
| 79 | - | t.Setenv("ZOT_HOME", home) | |
| 79 | + | t.Setenv("ZUT_HOME", home) | |
| 80 | 80 | ||
| 81 | 81 | src := filepath.Join(t.TempDir(), "theme-source") | |
| 82 | 82 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| @@ -102,7 +102,7 @@ func TestExtInstallThemeOnly(t *testing.T) { | |||
| 102 | 102 | ||
| 103 | 103 | func TestExtInstallRejectsMissingExecutable(t *testing.T) { | |
| 104 | 104 | home := t.TempDir() | |
| 105 | - | t.Setenv("ZOT_HOME", home) | |
| 105 | + | t.Setenv("ZUT_HOME", home) | |
| 106 | 106 | ||
| 107 | 107 | src := filepath.Join(t.TempDir(), "tasked-phases") | |
| 108 | 108 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| @@ -127,7 +127,7 @@ func TestExtInstallBuildGo(t *testing.T) { | |||
| 127 | 127 | } | |
| 128 | 128 | ||
| 129 | 129 | home := t.TempDir() | |
| 130 | - | t.Setenv("ZOT_HOME", home) | |
| 130 | + | t.Setenv("ZUT_HOME", home) | |
| 131 | 131 | src := filepath.Join(t.TempDir(), "go-extension") | |
| 132 | 132 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| 133 | 133 | t.Fatal(err) | |
| @@ -165,7 +165,7 @@ func TestExtInstallBuildFailureLeavesNoDestination(t *testing.T) { | |||
| 165 | 165 | } | |
| 166 | 166 | ||
| 167 | 167 | home := t.TempDir() | |
| 168 | - | t.Setenv("ZOT_HOME", home) | |
| 168 | + | t.Setenv("ZUT_HOME", home) | |
| 169 | 169 | src := filepath.Join(t.TempDir(), "broken-go") | |
| 170 | 170 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| 171 | 171 | t.Fatal(err) | |
| @@ -192,7 +192,7 @@ func TestExtInstallBuildFailureLeavesNoDestination(t *testing.T) { | |||
| 192 | 192 | t.Fatal(err) | |
| 193 | 193 | } | |
| 194 | 194 | for _, entry := range entries { | |
| 195 | - | if strings.HasPrefix(entry.Name(), ".zot-extension-install-") { | |
| 195 | + | if strings.HasPrefix(entry.Name(), ".zut-extension-install-") { | |
| 196 | 196 | t.Fatalf("failed build left staging directory %s", entry.Name()) | |
| 197 | 197 | } | |
| 198 | 198 | } | |
| @@ -240,7 +240,7 @@ func TestParseExtInstallArgs(t *testing.T) { | |||
| 240 | 240 | ||
| 241 | 241 | func TestExtInstallRejectsBuildForThemeOnly(t *testing.T) { | |
| 242 | 242 | home := t.TempDir() | |
| 243 | - | t.Setenv("ZOT_HOME", home) | |
| 243 | + | t.Setenv("ZUT_HOME", home) | |
| 244 | 244 | src := filepath.Join(t.TempDir(), "theme-source") | |
| 245 | 245 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| 246 | 246 | t.Fatal(err) | |
| @@ -263,7 +263,7 @@ func TestExtInstallRejectsBuildForThemeOnly(t *testing.T) { | |||
| 263 | 263 | ||
| 264 | 264 | func TestExtInstallRejectsWhitespaceManifestName(t *testing.T) { | |
| 265 | 265 | home := t.TempDir() | |
| 266 | - | t.Setenv("ZOT_HOME", home) | |
| 266 | + | t.Setenv("ZUT_HOME", home) | |
| 267 | 267 | src := filepath.Join(t.TempDir(), "whitespace-name") | |
| 268 | 268 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| 269 | 269 | t.Fatal(err) | |
| @@ -280,7 +280,7 @@ func TestExtInstallRejectsWhitespaceManifestName(t *testing.T) { | |||
| 280 | 280 | ||
| 281 | 281 | func TestExtInstallRejectsDotManifestName(t *testing.T) { | |
| 282 | 282 | home := t.TempDir() | |
| 283 | - | t.Setenv("ZOT_HOME", home) | |
| 283 | + | t.Setenv("ZUT_HOME", home) | |
| 284 | 284 | src := filepath.Join(t.TempDir(), "dot-name") | |
| 285 | 285 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| 286 | 286 | t.Fatal(err) | |
| @@ -297,7 +297,7 @@ func TestExtInstallRejectsDotManifestName(t *testing.T) { | |||
| 297 | 297 | ||
| 298 | 298 | func TestExtInstallRejectsBuildForPATHExecutable(t *testing.T) { | |
| 299 | 299 | home := t.TempDir() | |
| 300 | - | t.Setenv("ZOT_HOME", home) | |
| 300 | + | t.Setenv("ZUT_HOME", home) | |
| 301 | 301 | src := filepath.Join(t.TempDir(), "path-extension") | |
| 302 | 302 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| 303 | 303 | t.Fatal(err) | |
| @@ -321,7 +321,7 @@ func TestExtInstallRejectsNonExecutable(t *testing.T) { | |||
| 321 | 321 | } | |
| 322 | 322 | ||
| 323 | 323 | home := t.TempDir() | |
| 324 | - | t.Setenv("ZOT_HOME", home) | |
| 324 | + | t.Setenv("ZUT_HOME", home) | |
| 325 | 325 | src := filepath.Join(t.TempDir(), "non-executable") | |
| 326 | 326 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| 327 | 327 | t.Fatal(err) | |
| @@ -347,7 +347,7 @@ func TestExtInstallRejectsNonExecutable(t *testing.T) { | |||
| 347 | 347 | // the manifest name controls the installed directory. | |
| 348 | 348 | func TestExtInstallCopiesIgnoredExecutableAndUsesManifestName(t *testing.T) { | |
| 349 | 349 | home := t.TempDir() | |
| 350 | - | t.Setenv("ZOT_HOME", home) | |
| 350 | + | t.Setenv("ZUT_HOME", home) | |
| 351 | 351 | ||
| 352 | 352 | src := filepath.Join(t.TempDir(), "source-dir") | |
| 353 | 353 | if err := os.MkdirAll(src, 0o755); err != nil { | |
| @@ -434,7 +434,7 @@ func TestGitignoreNegation(t *testing.T) { | |||
| 434 | 434 | ||
| 435 | 435 | func TestExtDoctorStaticScanAndRender(t *testing.T) { | |
| 436 | 436 | home := t.TempDir() | |
| 437 | - | t.Setenv("ZOT_HOME", home) | |
| 437 | + | t.Setenv("ZUT_HOME", home) | |
| 438 | 438 | cwd := t.TempDir() | |
| 439 | 439 | oldCWD, err := os.Getwd() | |
| 440 | 440 | if err != nil { | |
| @@ -455,11 +455,11 @@ func TestExtDoctorStaticScanAndRender(t *testing.T) { | |||
| 455 | 455 | } | |
| 456 | 456 | } | |
| 457 | 457 | ||
| 458 | - | mustWrite(filepath.Join(cwd, ".zot", "extensions", "disabled", "extension.json"), `{"name":"disabled","exec":"./run.sh","enabled":false}`) | |
| 459 | - | mustWrite(filepath.Join(cwd, ".zot", "extensions", "theme", "extension.json"), `{"name":"theme"}`) | |
| 460 | - | mustWrite(filepath.Join(cwd, ".zot", "extensions", "theme", "theme.json"), `{"name":"Theme"}`) | |
| 461 | - | mustWrite(filepath.Join(cwd, ".zot", "extensions", "bad", "extension.json"), `{bad json`) | |
| 462 | - | mustWrite(filepath.Join(cwd, ".zot", "extensions", "dup", "extension.json"), `{"name":"dup","exec":"./project.sh"}`) | |
| 458 | + | mustWrite(filepath.Join(cwd, ".zut", "extensions", "disabled", "extension.json"), `{"name":"disabled","exec":"./run.sh","enabled":false}`) | |
| 459 | + | mustWrite(filepath.Join(cwd, ".zut", "extensions", "theme", "extension.json"), `{"name":"theme"}`) | |
| 460 | + | mustWrite(filepath.Join(cwd, ".zut", "extensions", "theme", "theme.json"), `{"name":"Theme"}`) | |
| 461 | + | mustWrite(filepath.Join(cwd, ".zut", "extensions", "bad", "extension.json"), `{bad json`) | |
| 462 | + | mustWrite(filepath.Join(cwd, ".zut", "extensions", "dup", "extension.json"), `{"name":"dup","exec":"./project.sh"}`) | |
| 463 | 463 | mustWrite(filepath.Join(home, "extensions", "dup", "extension.json"), `{"name":"dup","exec":"./global.sh"}`) | |
| 464 | 464 | ||
| 465 | 465 | rows := scanExtDoctorStatic() | |
M packages/agent/extensions/events.go +3 -3
| @@ -8,7 +8,7 @@ import ( | |||
| 8 | 8 | "time" | |
| 9 | 9 | "unicode/utf8" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 11 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // EmitEvent fires a one-way lifecycle event to every extension that | |
| @@ -304,7 +304,7 @@ func (m *Manager) askIntercept(ctx context.Context, ext *Extension, payload extp | |||
| 304 | 304 | ext.mu.Lock() | |
| 305 | 305 | delete(ext.pendingIntercept, id) | |
| 306 | 306 | ext.mu.Unlock() | |
| 307 | - | fmt.Fprintf(ext.logFile, "[zot] intercept write failed: %v\n", err) | |
| 307 | + | fmt.Fprintf(ext.logFile, "[zut] intercept write failed: %v\n", err) | |
| 308 | 308 | return InterceptResult{} | |
| 309 | 309 | } | |
| 310 | 310 | ||
| @@ -321,7 +321,7 @@ func (m *Manager) askIntercept(ctx context.Context, ext *Extension, payload extp | |||
| 321 | 321 | ext.mu.Lock() | |
| 322 | 322 | delete(ext.pendingIntercept, id) | |
| 323 | 323 | ext.mu.Unlock() | |
| 324 | - | fmt.Fprintf(ext.logFile, "[zot] intercept %s timed out; allowing\n", payload.Event) | |
| 324 | + | fmt.Fprintf(ext.logFile, "[zut] intercept %s timed out; allowing\n", payload.Event) | |
| 325 | 325 | return InterceptResult{} | |
| 326 | 326 | case <-ctx.Done(): | |
| 327 | 327 | ext.mu.Lock() | |
M packages/agent/extensions/intercept_test.go +3 -3
| @@ -39,7 +39,7 @@ func TestInterceptAllThreeEvents(t *testing.T) { | |||
| 39 | 39 | extDir := t.TempDir() | |
| 40 | 40 | script := `#!/bin/bash | |
| 41 | 41 | emit() { printf '%s\n' "$1"; } | |
| 42 | - | emit '{"type":"hello","name":"itest","version":"0.1.0","capabilities":["events"]}' | |
| 42 | + | emit '{"type":"hello","protocol_version":2,"name":"itest","version":"0.1.0","capabilities":["events"]}' | |
| 43 | 43 | emit '{"type":"subscribe","events":[],"intercept":["tool_call","turn_start","assistant_message"]}' | |
| 44 | 44 | emit '{"type":"ready"}' | |
| 45 | 45 | while IFS= read -r line; do | |
| @@ -83,7 +83,7 @@ done | |||
| 83 | 83 | t.Fatal(err) | |
| 84 | 84 | } | |
| 85 | 85 | ||
| 86 | - | // ZotHome is unused here; we load the extension explicitly. | |
| 86 | + | // ZutHome is unused here; we load the extension explicitly. | |
| 87 | 87 | m := New(t.TempDir(), "", "0.0.0-test", "anthropic", "claude-test", nil) | |
| 88 | 88 | t.Cleanup(func() { m.Stop(2 * time.Second) }) | |
| 89 | 89 | ||
| @@ -159,7 +159,7 @@ func TestReloadRespawnsExtensions(t *testing.T) { | |||
| 159 | 159 | extDir := t.TempDir() | |
| 160 | 160 | script := `#!/bin/bash | |
| 161 | 161 | emit() { printf '%s\n' "$1"; } | |
| 162 | - | emit '{"type":"hello","name":"rtest","version":"0.1.0","capabilities":["commands"]}' | |
| 162 | + | emit '{"type":"hello","protocol_version":2,"name":"rtest","version":"0.1.0","capabilities":["commands"]}' | |
| 163 | 163 | emit '{"type":"register_command","name":"rtest","description":"test"}' | |
| 164 | 164 | emit '{"type":"ready"}' | |
| 165 | 165 | while IFS= read -r line; do | |
M packages/agent/extensions/manager.go +40 -37
| @@ -1,11 +1,11 @@ | |||
| 1 | - | // Package extensions implements the host side of zot's subprocess | |
| 1 | + | // Package extensions implements the host side of zut's subprocess | |
| 2 | 2 | // extension protocol. The Manager discovers extensions in well-known | |
| 3 | 3 | // directories, spawns each one, completes the hello handshake, and | |
| 4 | 4 | // routes slash commands to the right extension. | |
| 5 | 5 | // | |
| 6 | - | // Each extension is its own process, communicating with zot over its | |
| 6 | + | // Each extension is its own process, communicating with zut over its | |
| 7 | 7 | // stdin/stdout in newline-delimited JSON. Stderr is redirected to a | |
| 8 | - | // per-extension log file under $ZOT_HOME/logs/. Crashing one | |
| 8 | + | // per-extension log file under $ZUT_HOME/logs/. Crashing one | |
| 9 | 9 | // extension does not affect the others or the host. | |
| 10 | 10 | // | |
| 11 | 11 | // See docs/extensions.md for the user-facing reference and | |
| @@ -28,13 +28,13 @@ import ( | |||
| 28 | 28 | "syscall" | |
| 29 | 29 | "time" | |
| 30 | 30 | ||
| 31 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 32 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 33 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 31 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 32 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 33 | + | "github.com/bnema/zut/packages/tui" | |
| 34 | 34 | ) | |
| 35 | 35 | ||
| 36 | 36 | // Manifest is the extension.json file shipped alongside an | |
| 37 | - | // extension's executable. It tells zot how to launch the extension | |
| 37 | + | // extension's executable. It tells zut how to launch the extension | |
| 38 | 38 | // and provides display metadata. | |
| 39 | 39 | type Manifest struct { | |
| 40 | 40 | Name string `json:"name"` | |
| @@ -49,7 +49,7 @@ type Manifest struct { | |||
| 49 | 49 | ||
| 50 | 50 | // IsEnabled returns the manifest's effective enabled state. Default | |
| 51 | 51 | // is true so adding a new extension folder Just Works without an | |
| 52 | - | // extra zot ext enable command. | |
| 52 | + | // extra zut ext enable command. | |
| 53 | 53 | func (m Manifest) IsEnabled() bool { | |
| 54 | 54 | return m.Enabled == nil || *m.Enabled | |
| 55 | 55 | } | |
| @@ -96,7 +96,7 @@ func loadManifestSkills(extDir string, mf Manifest) ([]*skills.Skill, []error) { | |||
| 96 | 96 | return loaded, append(errs, loadErrs...) | |
| 97 | 97 | } | |
| 98 | 98 | ||
| 99 | - | // Extension is a running extension subprocess and the metadata zot | |
| 99 | + | // Extension is a running extension subprocess and the metadata zut | |
| 100 | 100 | // tracks about it. | |
| 101 | 101 | type Extension struct { | |
| 102 | 102 | Manifest Manifest | |
| @@ -211,11 +211,11 @@ type commandRegistration struct { | |||
| 211 | 211 | name string | |
| 212 | 212 | } | |
| 213 | 213 | ||
| 214 | - | // Manager owns every extension subprocess for the lifetime of zot. | |
| 214 | + | // Manager owns every extension subprocess for the lifetime of zut. | |
| 215 | 215 | type Manager struct { | |
| 216 | - | zotHome string | |
| 216 | + | zutHome string | |
| 217 | 217 | cwd string | |
| 218 | - | zotVersion string | |
| 218 | + | zutVersion string | |
| 219 | 219 | provider string | |
| 220 | 220 | model string | |
| 221 | 221 | hooks HostHooks | |
| @@ -254,11 +254,11 @@ type sessionEventState struct { | |||
| 254 | 254 | ||
| 255 | 255 | // New constructs an empty Manager. Call Discover to populate it from | |
| 256 | 256 | // the on-disk extension directories. | |
| 257 | - | func New(zotHome, cwd, zotVersion, provider, model string, hooks HostHooks) *Manager { | |
| 257 | + | func New(zutHome, cwd, zutVersion, provider, model string, hooks HostHooks) *Manager { | |
| 258 | 258 | return &Manager{ | |
| 259 | - | zotHome: zotHome, | |
| 259 | + | zutHome: zutHome, | |
| 260 | 260 | cwd: cwd, | |
| 261 | - | zotVersion: zotVersion, | |
| 261 | + | zutVersion: zutVersion, | |
| 262 | 262 | provider: provider, | |
| 263 | 263 | model: model, | |
| 264 | 264 | hooks: hooks, | |
| @@ -324,10 +324,10 @@ func (m *Manager) Discover(ctx context.Context) []error { | |||
| 324 | 324 | func (m *Manager) searchDirs() []string { | |
| 325 | 325 | var dirs []string | |
| 326 | 326 | if m.cwd != "" { | |
| 327 | - | dirs = append(dirs, filepath.Join(m.cwd, ".zot", "extensions")) | |
| 327 | + | dirs = append(dirs, filepath.Join(m.cwd, ".zut", "extensions")) | |
| 328 | 328 | } | |
| 329 | - | if m.zotHome != "" { | |
| 330 | - | dirs = append(dirs, filepath.Join(m.zotHome, "extensions")) | |
| 329 | + | if m.zutHome != "" { | |
| 330 | + | dirs = append(dirs, filepath.Join(m.zutHome, "extensions")) | |
| 331 | 331 | } | |
| 332 | 332 | return dirs | |
| 333 | 333 | } | |
| @@ -352,7 +352,7 @@ func (m *Manager) loadOne(ctx context.Context, dir string) error { | |||
| 352 | 352 | return errors.New("manifest: exec is required") | |
| 353 | 353 | } | |
| 354 | 354 | if !mf.IsEnabled() { | |
| 355 | - | // Quietly skip disabled extensions; zot ext list will show them. | |
| 355 | + | // Quietly skip disabled extensions; zut ext list will show them. | |
| 356 | 356 | return nil | |
| 357 | 357 | } | |
| 358 | 358 | ||
| @@ -400,8 +400,8 @@ func (m *Manager) loadOne(ctx context.Context, dir string) error { | |||
| 400 | 400 | } | |
| 401 | 401 | ||
| 402 | 402 | // LoadExplicit loads each path as an ad-hoc extension. Used for | |
| 403 | - | // `zot --ext <path>` so extension authors can iterate on a working | |
| 404 | - | // copy without having to `zot ext install` after every change. | |
| 403 | + | // `zut --ext <path>` so extension authors can iterate on a working | |
| 404 | + | // copy without having to `zut ext install` after every change. | |
| 405 | 405 | // | |
| 406 | 406 | // Loaded BEFORE Discover so explicit paths win on name conflicts | |
| 407 | 407 | // against installed extensions. Spawns happen in parallel like the | |
| @@ -540,7 +540,7 @@ func (m *Manager) Reload(ctx context.Context, grace time.Duration) ReloadStats { | |||
| 540 | 540 | stats.Loaded = len(m.ext) | |
| 541 | 541 | m.mu.RUnlock() | |
| 542 | 542 | ||
| 543 | - | // Wait for ready frames. Use the same 3s grace zot uses at | |
| 543 | + | // Wait for ready frames. Use the same 3s grace zut uses at | |
| 544 | 544 | // startup so the reload feels no slower than a cold boot. | |
| 545 | 545 | readyDeadline := time.Now().Add(grace) | |
| 546 | 546 | if time.Until(readyDeadline) < 3*time.Second { | |
| @@ -570,7 +570,7 @@ func (m *Manager) Reload(ctx context.Context, grace time.Duration) ReloadStats { | |||
| 570 | 570 | // | |
| 571 | 571 | // Waits run in parallel: total time is max(per-extension wait), not | |
| 572 | 572 | // sum. Without this, a single slow extension (e.g. `npx tsx` cold) | |
| 573 | - | // would gate every other extension's wait too and zot startup would | |
| 573 | + | // would gate every other extension's wait too and zut startup would | |
| 574 | 574 | // scale linearly with the number of slow runtimes installed. | |
| 575 | 575 | // | |
| 576 | 576 | // Call after Discover and before relying on tool registrations. | |
| @@ -595,7 +595,7 @@ func (m *Manager) WaitForReady(grace time.Duration) { | |||
| 595 | 595 | ext.readyTimedOut = true | |
| 596 | 596 | ext.diagnostics = append(ext.diagnostics, "timed out waiting for ready frame") | |
| 597 | 597 | ext.mu.Unlock() | |
| 598 | - | fmt.Fprintf(ext.logFile, "[zot] timed out waiting for ready frame; proceeding\n") | |
| 598 | + | fmt.Fprintf(ext.logFile, "[zut] timed out waiting for ready frame; proceeding\n") | |
| 599 | 599 | ext.readyOnce.Do(func() { close(ext.readyCh) }) | |
| 600 | 600 | } | |
| 601 | 601 | }(ext) | |
| @@ -628,7 +628,7 @@ func ResolveExecPath(extDir, execName string) (string, bool) { | |||
| 628 | 628 | // runs the synchronous portion of the hello handshake. Asynchronous | |
| 629 | 629 | // frames are processed in a goroutine started here. | |
| 630 | 630 | func (m *Manager) spawn(ctx context.Context, ext *Extension) error { | |
| 631 | - | logsDir := filepath.Join(m.zotHome, "logs") | |
| 631 | + | logsDir := filepath.Join(m.zutHome, "logs") | |
| 632 | 632 | _ = os.MkdirAll(logsDir, 0o755) | |
| 633 | 633 | logPath := filepath.Join(logsDir, "ext-"+ext.Manifest.Name+".log") | |
| 634 | 634 | logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644) | |
| @@ -637,7 +637,7 @@ func (m *Manager) spawn(ctx context.Context, ext *Extension) error { | |||
| 637 | 637 | } | |
| 638 | 638 | ext.LogPath = logPath | |
| 639 | 639 | ext.logFile = logFile | |
| 640 | - | fmt.Fprintf(logFile, "\n[zot] starting %s/%s at %s\n", ext.Manifest.Name, ext.Manifest.Version, time.Now().Format(time.RFC3339)) | |
| 640 | + | fmt.Fprintf(logFile, "\n[zut] starting %s/%s at %s\n", ext.Manifest.Name, ext.Manifest.Version, time.Now().Format(time.RFC3339)) | |
| 641 | 641 | ||
| 642 | 642 | // Bare names (node, npx, python3, tsx, etc.) are looked up via PATH; | |
| 643 | 643 | // absolute and extension-relative paths are resolved by the helper. | |
| @@ -707,6 +707,9 @@ func (m *Manager) spawn(ctx context.Context, ext *Extension) error { | |||
| 707 | 707 | if hello.Type != "hello" || hello.Name == "" { | |
| 708 | 708 | return fmt.Errorf("first frame must be hello (got %q; stderr log: %s)", hello.Type, logPath) | |
| 709 | 709 | } | |
| 710 | + | if hello.ProtocolVersion != extproto.ProtocolVersion { | |
| 711 | + | return fmt.Errorf("unsupported extension protocol version %d (want %d; stderr log: %s)", hello.ProtocolVersion, extproto.ProtocolVersion, logPath) | |
| 712 | + | } | |
| 710 | 713 | // Trust the manifest's name; ignore mismatch from the hello. | |
| 711 | 714 | ext.helloAck = true | |
| 712 | 715 | ||
| @@ -719,7 +722,7 @@ func (m *Manager) spawn(ctx context.Context, ext *Extension) error { | |||
| 719 | 722 | ack, _ := extproto.Encode(extproto.HelloAckFromHost{ | |
| 720 | 723 | Type: "hello_ack", | |
| 721 | 724 | ProtocolVersion: extproto.ProtocolVersion, | |
| 722 | - | ZotVersion: m.zotVersion, | |
| 725 | + | ZutVersion: m.zutVersion, | |
| 723 | 726 | Provider: m.provider, | |
| 724 | 727 | Model: m.model, | |
| 725 | 728 | CWD: m.cwd, | |
| @@ -800,7 +803,7 @@ func (m *Manager) assumeReadyAfterIdle(ext *Extension) { | |||
| 800 | 803 | ext.autoReady = true | |
| 801 | 804 | ext.diagnostics = append(ext.diagnostics, "no ready frame; auto-ready after idle") | |
| 802 | 805 | ext.mu.Unlock() | |
| 803 | - | fmt.Fprintf(ext.logFile, "[zot] no ready frame; auto-readying after idle (legacy SDK?)\n") | |
| 806 | + | fmt.Fprintf(ext.logFile, "[zut] no ready frame; auto-readying after idle (legacy SDK?)\n") | |
| 804 | 807 | close(ext.readyCh) | |
| 805 | 808 | }) | |
| 806 | 809 | return | |
| @@ -833,7 +836,7 @@ func (m *Manager) readLoop(ext *Extension, scanner *bufio.Scanner) { | |||
| 833 | 836 | } | |
| 834 | 837 | ext.stopLifecycleWriter() | |
| 835 | 838 | ext.readyOnce.Do(func() { close(ext.readyCh) }) | |
| 836 | - | fmt.Fprintf(ext.logFile, "[zot] extension %s read loop exited at %s\n", ext.Manifest.Name, time.Now().Format(time.RFC3339)) | |
| 839 | + | fmt.Fprintf(ext.logFile, "[zut] extension %s read loop exited at %s\n", ext.Manifest.Name, time.Now().Format(time.RFC3339)) | |
| 837 | 840 | }() | |
| 838 | 841 | ||
| 839 | 842 | for scanner.Scan() { | |
| @@ -844,7 +847,7 @@ func (m *Manager) readLoop(ext *Extension, scanner *bufio.Scanner) { | |||
| 844 | 847 | var frame extproto.Frame | |
| 845 | 848 | if err := json.Unmarshal(line, &frame); err != nil { | |
| 846 | 849 | ext.recordDiagnostic(fmt.Sprintf("malformed json frame: %v", err)) | |
| 847 | - | fmt.Fprintf(ext.logFile, "[zot] malformed json from extension: %v\n", err) | |
| 850 | + | fmt.Fprintf(ext.logFile, "[zut] malformed json from extension: %v\n", err) | |
| 848 | 851 | continue | |
| 849 | 852 | } | |
| 850 | 853 | switch frame.Type { | |
| @@ -870,7 +873,7 @@ func (m *Manager) readLoop(ext *Extension, scanner *bufio.Scanner) { | |||
| 870 | 873 | case "register_tool": | |
| 871 | 874 | var rt extproto.RegisterToolFromExt | |
| 872 | 875 | if err := json.Unmarshal(line, &rt); err != nil { | |
| 873 | - | fmt.Fprintf(ext.logFile, "[zot] bad register_tool frame: %v\n", err) | |
| 876 | + | fmt.Fprintf(ext.logFile, "[zut] bad register_tool frame: %v\n", err) | |
| 874 | 877 | continue | |
| 875 | 878 | } | |
| 876 | 879 | // Validate the schema parses as JSON. If not, refuse to | |
| @@ -879,7 +882,7 @@ func (m *Manager) readLoop(ext *Extension, scanner *bufio.Scanner) { | |||
| 879 | 882 | var tmp any | |
| 880 | 883 | if err := json.Unmarshal(rt.Schema, &tmp); err != nil { | |
| 881 | 884 | ext.recordDiagnostic(fmt.Sprintf("tool %q schema is not valid json: %v", rt.Name, err)) | |
| 882 | - | fmt.Fprintf(ext.logFile, "[zot] tool %q: schema is not valid json (%v); skipped\n", rt.Name, err) | |
| 885 | + | fmt.Fprintf(ext.logFile, "[zut] tool %q: schema is not valid json (%v); skipped\n", rt.Name, err) | |
| 883 | 886 | continue | |
| 884 | 887 | } | |
| 885 | 888 | } | |
| @@ -993,7 +996,7 @@ func (m *Manager) readLoop(ext *Extension, scanner *bufio.Scanner) { | |||
| 993 | 996 | if strings.HasPrefix(text, "/") && m.hooks != nil { | |
| 994 | 997 | m.hooks.SubmitSlash(text) | |
| 995 | 998 | } else { | |
| 996 | - | fmt.Fprintf(ext.logFile, "[zot] submit_slash refused (not a slash command): %q\n", s.Text) | |
| 999 | + | fmt.Fprintf(ext.logFile, "[zut] submit_slash refused (not a slash command): %q\n", s.Text) | |
| 997 | 1000 | } | |
| 998 | 1001 | } | |
| 999 | 1002 | case "command_response": | |
| @@ -1031,7 +1034,7 @@ func (m *Manager) readLoop(ext *Extension, scanner *bufio.Scanner) { | |||
| 1031 | 1034 | // Caller of Stop is waiting on the process exit, not this frame. | |
| 1032 | 1035 | default: | |
| 1033 | 1036 | ext.recordDiagnostic(fmt.Sprintf("unknown frame type %q", frame.Type)) | |
| 1034 | - | fmt.Fprintf(ext.logFile, "[zot] unknown frame type %q\n", frame.Type) | |
| 1037 | + | fmt.Fprintf(ext.logFile, "[zut] unknown frame type %q\n", frame.Type) | |
| 1035 | 1038 | } | |
| 1036 | 1039 | } | |
| 1037 | 1040 | } | |
| @@ -1177,7 +1180,7 @@ type ExtensionDiagnostic struct { | |||
| 1177 | 1180 | } | |
| 1178 | 1181 | ||
| 1179 | 1182 | // Diagnostics returns a snapshot of loaded extension state for commands such | |
| 1180 | - | // as `zot ext doctor`. It is read-only and does not change manager behavior. | |
| 1183 | + | // as `zut ext doctor`. It is read-only and does not change manager behavior. | |
| 1181 | 1184 | func (m *Manager) Diagnostics() []ExtensionDiagnostic { | |
| 1182 | 1185 | m.mu.RLock() | |
| 1183 | 1186 | defer m.mu.RUnlock() | |
| @@ -1253,7 +1256,7 @@ func (m *Manager) Commands() []CommandInfo { | |||
| 1253 | 1256 | } | |
| 1254 | 1257 | ||
| 1255 | 1258 | // CommandInfo is one extension-registered slash command, surfaced to | |
| 1256 | - | // the rest of zot for display purposes. | |
| 1259 | + | // the rest of zut for display purposes. | |
| 1257 | 1260 | type CommandInfo struct { | |
| 1258 | 1261 | Extension string | |
| 1259 | 1262 | Name string | |
| @@ -1509,7 +1512,7 @@ func stopExtensions(exts []*Extension, gracePeriod time.Duration) { | |||
| 1509 | 1512 | } | |
| 1510 | 1513 | ||
| 1511 | 1514 | // All returns every extension currently tracked, enabled or not. | |
| 1512 | - | // Used by `zot ext list`. | |
| 1515 | + | // Used by `zut ext list`. | |
| 1513 | 1516 | func (m *Manager) All() []*Extension { | |
| 1514 | 1517 | m.mu.RLock() | |
| 1515 | 1518 | defer m.mu.RUnlock() | |
M packages/agent/extensions/manager_test.go +36 -7
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "io" | |
| 7 | 7 | "os" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 9 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 10 | 10 | "path/filepath" | |
| 11 | 11 | "runtime" | |
| 12 | 12 | "strings" | |
| @@ -123,7 +123,7 @@ func writeMockExtension(t *testing.T, root string) { | |||
| 123 | 123 | // stdin closes; tail's -F keeps the pipe alive long enough for | |
| 124 | 124 | // the manager to send command_invoked. | |
| 125 | 125 | script := `#!/bin/sh | |
| 126 | - | printf '%s\n' '{"type":"hello","name":"mock","version":"0.0.1","capabilities":["commands"]}' | |
| 126 | + | printf '%s\n' '{"type":"hello","protocol_version":2,"name":"mock","version":"0.0.1","capabilities":["commands"]}' | |
| 127 | 127 | printf '%s\n' '{"type":"register_command","name":"Ping","description":"ping/pong"}' | |
| 128 | 128 | while IFS= read -r line; do | |
| 129 | 129 | case "$line" in | |
| @@ -260,6 +260,35 @@ func TestSpawnCleansUpProcessAfterInvalidHello(t *testing.T) { | |||
| 260 | 260 | } | |
| 261 | 261 | } | |
| 262 | 262 | ||
| 263 | + | func TestSpawnRejectsUnsupportedExtensionProtocolVersion(t *testing.T) { | |
| 264 | + | if runtime.GOOS == "windows" { | |
| 265 | + | t.Skip("mock extension uses /bin/sh; skip on windows") | |
| 266 | + | } | |
| 267 | + | ||
| 268 | + | tmp := t.TempDir() | |
| 269 | + | extDir := filepath.Join(tmp, "broken") | |
| 270 | + | if err := os.MkdirAll(extDir, 0o755); err != nil { | |
| 271 | + | t.Fatal(err) | |
| 272 | + | } | |
| 273 | + | script := `#!/bin/sh | |
| 274 | + | printf '%s\n' '{"type":"hello","protocol_version":1,"name":"broken"}' | |
| 275 | + | while IFS= read -r line; do :; done | |
| 276 | + | ` | |
| 277 | + | if err := os.WriteFile(filepath.Join(extDir, "run.sh"), []byte(script), 0o755); err != nil { | |
| 278 | + | t.Fatal(err) | |
| 279 | + | } | |
| 280 | + | ||
| 281 | + | ext := &Extension{Manifest: Manifest{Name: "broken", Exec: "./run.sh"}, Dir: extDir} | |
| 282 | + | mgr := New(tmp, "", "0.0.0-test", "", "", nil) | |
| 283 | + | err := mgr.spawn(context.Background(), ext) | |
| 284 | + | if err == nil || !strings.Contains(err.Error(), "unsupported extension protocol version 1") { | |
| 285 | + | t.Fatalf("spawn error = %v", err) | |
| 286 | + | } | |
| 287 | + | if ext.cmd == nil || ext.cmd.ProcessState == nil { | |
| 288 | + | t.Fatal("failed protocol handshake process was not reaped") | |
| 289 | + | } | |
| 290 | + | } | |
| 291 | + | ||
| 263 | 292 | func TestDiscoverLoadsThemeOnlyExtension(t *testing.T) { | |
| 264 | 293 | tmp := t.TempDir() | |
| 265 | 294 | extDir := filepath.Join(tmp, "extensions", "theme-only") | |
| @@ -423,7 +452,7 @@ func TestDiagnosticsReportMalformedFramesAndConflicts(t *testing.T) { | |||
| 423 | 452 | } | |
| 424 | 453 | ||
| 425 | 454 | writeDiagExtension("a-first", `#!/bin/sh | |
| 426 | - | printf '%s\n' '{"type":"hello","name":"a-first","version":"0.1","capabilities":["commands","tools"]}' | |
| 455 | + | printf '%s\n' '{"type":"hello","protocol_version":2,"name":"a-first","version":"0.1","capabilities":["commands","tools"]}' | |
| 427 | 456 | printf '%s\n' '{"type":"register_command","name":"CaseTest","description":"first"}' | |
| 428 | 457 | printf '%s\n' '{"type":"register_tool","name":"shared","description":"first","schema":{"type":"object"}}' | |
| 429 | 458 | printf '%s\n' '{"type":"register_tool","name":"broken","description":"bad","schema":' | |
| @@ -435,7 +464,7 @@ while IFS= read -r line; do | |||
| 435 | 464 | done | |
| 436 | 465 | `) | |
| 437 | 466 | writeDiagExtension("b-second", `#!/bin/sh | |
| 438 | - | printf '%s\n' '{"type":"hello","name":"b-second","version":"0.1","capabilities":["commands","tools"]}' | |
| 467 | + | printf '%s\n' '{"type":"hello","protocol_version":2,"name":"b-second","version":"0.1","capabilities":["commands","tools"]}' | |
| 439 | 468 | printf '%s\n' '{"type":"register_command","name":"casetest","description":"second"}' | |
| 440 | 469 | printf '%s\n' '{"type":"register_tool","name":"shared","description":"second","schema":{"type":"object"}}' | |
| 441 | 470 | printf '%s\n' '{"type":"ready"}' | |
| @@ -512,7 +541,7 @@ func TestSpontaneousSubmit(t *testing.T) { | |||
| 512 | 541 | } | |
| 513 | 542 | ||
| 514 | 543 | script := `#!/bin/sh | |
| 515 | - | printf '%s\n' '{"type":"hello","name":"submit-mock","version":"0.1","capabilities":["submit"]}' | |
| 544 | + | printf '%s\n' '{"type":"hello","protocol_version":2,"name":"submit-mock","version":"0.1","capabilities":["submit"]}' | |
| 516 | 545 | printf '%s\n' '{"type":"ready"}' | |
| 517 | 546 | printf '%s\n' '{"type":"submit","text":" explain this repository briefly "}' | |
| 518 | 547 | printf '%s\n' '{"type":"submit","text":" "}' | |
| @@ -575,7 +604,7 @@ func TestSpontaneousAlert(t *testing.T) { | |||
| 575 | 604 | } | |
| 576 | 605 | ||
| 577 | 606 | script := `#!/bin/sh | |
| 578 | - | printf '%s\n' '{"type":"hello","name":"alert-mock","version":"0.1","capabilities":["alerts"]}' | |
| 607 | + | printf '%s\n' '{"type":"hello","protocol_version":2,"name":"alert-mock","version":"0.1","capabilities":["alerts"]}' | |
| 579 | 608 | printf '%s\n' '{"type":"ready"}' | |
| 580 | 609 | printf '%s\n' '{"type":"alert","kind":"bell","reason":"question_ready"}' | |
| 581 | 610 | while IFS= read -r line; do | |
| @@ -643,7 +672,7 @@ func TestSpontaneousOpenPanel(t *testing.T) { | |||
| 643 | 672 | // Extension emits hello + ready, then immediately fires a | |
| 644 | 673 | // spontaneous open_panel, then waits for shutdown. | |
| 645 | 674 | script := `#!/bin/sh | |
| 646 | - | printf '%s\n' '{"type":"hello","name":"panel-mock","version":"0.1","capabilities":["panels"]}' | |
| 675 | + | printf '%s\n' '{"type":"hello","protocol_version":2,"name":"panel-mock","version":"0.1","capabilities":["panels"]}' | |
| 647 | 676 | printf '%s\n' '{"type":"ready"}' | |
| 648 | 677 | printf '%s\n' '{"type":"open_panel","panel":{"id":"test-panel","title":"Hello Panel","lines":["line one","line two"],"footer":"esc close"}}' | |
| 649 | 678 | while IFS= read -r line; do | |
M packages/agent/extensions/proc_unix.go +3 -3
| @@ -7,12 +7,12 @@ import ( | |||
| 7 | 7 | "syscall" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | - | // isolateExtensionProcess moves extension subprocesses out of zot's | |
| 10 | + | // isolateExtensionProcess moves extension subprocesses out of zut's | |
| 11 | 11 | // foreground process group. Some terminals, including kitty, inspect | |
| 12 | 12 | // foreground-process-group members when deciding the cwd for "open new | |
| 13 | 13 | // tab/split here". Extensions run with cmd.Dir set to their own install | |
| 14 | - | // directory, so leaving them in zot's process group can make the terminal | |
| 15 | - | // believe the tab cwd is an extension directory even after zot reports the | |
| 14 | + | // directory, so leaving them in zut's process group can make the terminal | |
| 15 | + | // believe the tab cwd is an extension directory even after zut reports the | |
| 16 | 16 | // project cwd with OSC 7. | |
| 17 | 17 | func isolateExtensionProcess(cmd *exec.Cmd) { | |
| 18 | 18 | cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} | |
M packages/agent/extensions/tool.go +2 -2
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "fmt" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // extensionTool wraps a single extension-registered tool as a | |
M packages/agent/extensions/tool_test.go +1 -1
| @@ -23,7 +23,7 @@ func writeMockToolExtension(t *testing.T, root string) { | |||
| 23 | 23 | // Shell-script extension: hello, register one tool, ready, then | |
| 24 | 24 | // loop on stdin echoing back tool_call args as tool_result text. | |
| 25 | 25 | script := `#!/bin/sh | |
| 26 | - | printf '%s\n' '{"type":"hello","name":"tool-mock","version":"0.1","capabilities":["tools"]}' | |
| 26 | + | printf '%s\n' '{"type":"hello","protocol_version":2,"name":"tool-mock","version":"0.1","capabilities":["tools"]}' | |
| 27 | 27 | printf '%s\n' '{"type":"register_tool","name":"echo","description":"echo back the args","schema":{"type":"object","properties":{"msg":{"type":"string"}}}}' | |
| 28 | 28 | printf '%s\n' '{"type":"ready"}' | |
| 29 | 29 | while IFS= read -r line; do | |
M packages/agent/extproto/extproto.go +12 -9
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | // Package extproto defines the JSON-over-stdin/stdout wire format | |
| 2 | - | // spoken between zot and its extension subprocesses. Both the host | |
| 2 | + | // spoken between zut and its extension subprocesses. Both the host | |
| 3 | 3 | // (packages/agent/extensions) and the SDK (packages/agent/ext) marshal/ | |
| 4 | 4 | // unmarshal the same types, so changes here ripple through both. | |
| 5 | 5 | // | |
| @@ -7,8 +7,8 @@ | |||
| 7 | 7 | // boundaries follow newline boundaries; no multi-line JSON. | |
| 8 | 8 | // | |
| 9 | 9 | // Direction conventions in this file: | |
| 10 | - | // - Type names ending in "FromExt" are sent by the extension to zot. | |
| 11 | - | // - Type names ending in "FromHost" are sent by zot to the extension. | |
| 10 | + | // - Type names ending in "FromExt" are sent by the extension to zut. | |
| 11 | + | // - Type names ending in "FromHost" are sent by zut to the extension. | |
| 12 | 12 | // - Names without a suffix are direction-neutral payloads or shared | |
| 13 | 13 | // value types. | |
| 14 | 14 | // | |
| @@ -22,7 +22,9 @@ import ( | |||
| 22 | 22 | "strings" | |
| 23 | 23 | ) | |
| 24 | 24 | ||
| 25 | - | const ProtocolVersion = 1 | |
| 25 | + | // ProtocolVersion is the extension protocol major. Version 2 introduces the | |
| 26 | + | // zut_version wire key and requires both sides to reject mismatched majors. | |
| 27 | + | const ProtocolVersion = 2 | |
| 26 | 28 | ||
| 27 | 29 | type Frame struct { | |
| 28 | 30 | Type string `json:"type"` | |
| @@ -30,10 +32,11 @@ type Frame struct { | |||
| 30 | 32 | } | |
| 31 | 33 | ||
| 32 | 34 | type HelloFromExt struct { | |
| 33 | - | Type string `json:"type"` | |
| 34 | - | Name string `json:"name"` | |
| 35 | - | Version string `json:"version"` | |
| 36 | - | Capabilities []string `json:"capabilities,omitempty"` | |
| 35 | + | Type string `json:"type"` | |
| 36 | + | ProtocolVersion int `json:"protocol_version"` | |
| 37 | + | Name string `json:"name"` | |
| 38 | + | Version string `json:"version"` | |
| 39 | + | Capabilities []string `json:"capabilities,omitempty"` | |
| 37 | 40 | } | |
| 38 | 41 | ||
| 39 | 42 | type RegisterCommandFromExt struct { | |
| @@ -228,7 +231,7 @@ type ShutdownAckFromExt struct { | |||
| 228 | 231 | type HelloAckFromHost struct { | |
| 229 | 232 | Type string `json:"type"` | |
| 230 | 233 | ProtocolVersion int `json:"protocol_version"` | |
| 231 | - | ZotVersion string `json:"zot_version"` | |
| 234 | + | ZutVersion string `json:"zut_version"` | |
| 232 | 235 | Provider string `json:"provider"` | |
| 233 | 236 | Model string `json:"model"` | |
| 234 | 237 | CWD string `json:"cwd"` | |
M packages/agent/extupdate.go +8 -8
| @@ -13,7 +13,7 @@ import ( | |||
| 13 | 13 | "time" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | - | // updateAllExtensions iterates $ZOT_HOME/extensions/* and tries to | |
| 16 | + | // updateAllExtensions iterates $ZUT_HOME/extensions/* and tries to | |
| 17 | 17 | // update each one in place via `git pull --ff-only`. Failures are | |
| 18 | 18 | // per-extension and never abort the loop; the caller can treat any | |
| 19 | 19 | // error here as advisory only. | |
| @@ -38,14 +38,14 @@ import ( | |||
| 38 | 38 | // remote git URL would be a real footgun. Extension authors are | |
| 39 | 39 | // expected to commit a working binary (or instruct the user to | |
| 40 | 40 | // rebuild manually via /reload-ext + their own build). | |
| 41 | - | func updateAllExtensions(zotHome string) { | |
| 42 | - | dir := filepath.Join(zotHome, "extensions") | |
| 41 | + | func updateAllExtensions(zutHome string) { | |
| 42 | + | dir := filepath.Join(zutHome, "extensions") | |
| 43 | 43 | entries, err := os.ReadDir(dir) | |
| 44 | 44 | if err != nil { | |
| 45 | 45 | if os.IsNotExist(err) { | |
| 46 | 46 | return // no extensions installed; nothing to do | |
| 47 | 47 | } | |
| 48 | - | fmt.Fprintf(os.Stderr, "zot update: skipping extension update (read %s: %v)\n", dir, err) | |
| 48 | + | fmt.Fprintf(os.Stderr, "zut update: skipping extension update (read %s: %v)\n", dir, err) | |
| 49 | 49 | return | |
| 50 | 50 | } | |
| 51 | 51 | ||
| @@ -67,7 +67,7 @@ func updateAllExtensions(zotHome string) { | |||
| 67 | 67 | } | |
| 68 | 68 | ||
| 69 | 69 | fmt.Println() | |
| 70 | - | fmt.Println("zot update: updating extensions...") | |
| 70 | + | fmt.Println("zut update: updating extensions...") | |
| 71 | 71 | ||
| 72 | 72 | var updated, upToDate, skipped, failed int | |
| 73 | 73 | for _, name := range names { | |
| @@ -85,7 +85,7 @@ func updateAllExtensions(zotHome string) { | |||
| 85 | 85 | } | |
| 86 | 86 | } | |
| 87 | 87 | ||
| 88 | - | fmt.Printf("zot update: extensions: %d updated, %d up-to-date, %d skipped, %d failed\n", | |
| 88 | + | fmt.Printf("zut update: extensions: %d updated, %d up-to-date, %d skipped, %d failed\n", | |
| 89 | 89 | updated, upToDate, skipped, failed) | |
| 90 | 90 | } | |
| 91 | 91 | ||
| @@ -180,7 +180,7 @@ func updateOneExtension(extDir, name string) string { | |||
| 180 | 180 | func gitStash(ctx context.Context, dir string) (stashRef string, stashed bool, err error) { | |
| 181 | 181 | // Tag the stash with a recognisable name so the user can find it | |
| 182 | 182 | // later if pop fails and they want to inspect it. | |
| 183 | - | msg := fmt.Sprintf("zot-update-%d", time.Now().Unix()) | |
| 183 | + | msg := fmt.Sprintf("zut-update-%d", time.Now().Unix()) | |
| 184 | 184 | out, err := runGit(ctx, dir, "stash", "push", "--include-untracked", "-m", msg) | |
| 185 | 185 | if err != nil { | |
| 186 | 186 | return "", false, fmt.Errorf("%s", strings.TrimSpace(out)) | |
| @@ -196,7 +196,7 @@ func gitStash(ctx context.Context, dir string) (stashRef string, stashed bool, e | |||
| 196 | 196 | if lerr == nil { | |
| 197 | 197 | for _, line := range strings.Split(listOut, "\n") { | |
| 198 | 198 | if strings.Contains(line, msg) { | |
| 199 | - | // line looks like: "stash@{0}: On main: zot-update-1700000000" | |
| 199 | + | // line looks like: "stash@{0}: On main: zut-update-1700000000" | |
| 200 | 200 | if idx := strings.Index(line, ":"); idx > 0 { | |
| 201 | 201 | return strings.TrimSpace(line[:idx]), true, nil | |
| 202 | 202 | } | |
M packages/agent/extupdate_test.go +13 -13
| @@ -66,8 +66,8 @@ func mustRun(t *testing.T, dir, name string, args ...string) { | |||
| 66 | 66 | } | |
| 67 | 67 | } | |
| 68 | 68 | ||
| 69 | - | // makeFakeZotHome scaffolds $ZOT_HOME/extensions/ and returns the path. | |
| 70 | - | func makeFakeZotHome(t *testing.T) string { | |
| 69 | + | // makeFakeZutHome scaffolds $ZUT_HOME/extensions/ and returns the path. | |
| 70 | + | func makeFakeZutHome(t *testing.T) string { | |
| 71 | 71 | t.Helper() | |
| 72 | 72 | home := t.TempDir() | |
| 73 | 73 | if err := os.MkdirAll(filepath.Join(home, "extensions"), 0o755); err != nil { | |
| @@ -95,19 +95,19 @@ func extWithManifest(t *testing.T, root, name, content string) string { | |||
| 95 | 95 | // ----- tests ----- | |
| 96 | 96 | ||
| 97 | 97 | func TestUpdateAllExtensions_NoExtensionsDirectory(t *testing.T) { | |
| 98 | - | // Brand new $ZOT_HOME with no extensions/ directory at all. | |
| 98 | + | // Brand new $ZUT_HOME with no extensions/ directory at all. | |
| 99 | 99 | // Must not panic and must print nothing alarming. | |
| 100 | 100 | home := t.TempDir() | |
| 101 | 101 | updateAllExtensions(home) // no-op | |
| 102 | 102 | } | |
| 103 | 103 | ||
| 104 | 104 | func TestUpdateAllExtensions_EmptyDirectory(t *testing.T) { | |
| 105 | - | home := makeFakeZotHome(t) | |
| 105 | + | home := makeFakeZutHome(t) | |
| 106 | 106 | updateAllExtensions(home) // no-op, no panic | |
| 107 | 107 | } | |
| 108 | 108 | ||
| 109 | 109 | func TestUpdateOneExtension_NoManifest(t *testing.T) { | |
| 110 | - | home := makeFakeZotHome(t) | |
| 110 | + | home := makeFakeZutHome(t) | |
| 111 | 111 | dir := filepath.Join(home, "extensions", "noman") | |
| 112 | 112 | _ = os.MkdirAll(dir, 0o755) | |
| 113 | 113 | got := updateOneExtension(dir, "noman") | |
| @@ -117,7 +117,7 @@ func TestUpdateOneExtension_NoManifest(t *testing.T) { | |||
| 117 | 117 | } | |
| 118 | 118 | ||
| 119 | 119 | func TestUpdateOneExtension_Disabled(t *testing.T) { | |
| 120 | - | home := makeFakeZotHome(t) | |
| 120 | + | home := makeFakeZutHome(t) | |
| 121 | 121 | dir := extWithManifest(t, home, "off", | |
| 122 | 122 | `{"name":"off","version":"1.0.0","exec":"./x","enabled":false}`) | |
| 123 | 123 | got := updateOneExtension(dir, "off") | |
| @@ -127,7 +127,7 @@ func TestUpdateOneExtension_Disabled(t *testing.T) { | |||
| 127 | 127 | } | |
| 128 | 128 | ||
| 129 | 129 | func TestUpdateOneExtension_NotAGitCheckout(t *testing.T) { | |
| 130 | - | home := makeFakeZotHome(t) | |
| 130 | + | home := makeFakeZutHome(t) | |
| 131 | 131 | dir := extWithManifest(t, home, "plain", "") | |
| 132 | 132 | got := updateOneExtension(dir, "plain") | |
| 133 | 133 | if got != "skipped" { | |
| @@ -138,7 +138,7 @@ func TestUpdateOneExtension_NotAGitCheckout(t *testing.T) { | |||
| 138 | 138 | func TestUpdateOneExtension_UpToDate(t *testing.T) { | |
| 139 | 139 | gitAvailable(t) | |
| 140 | 140 | tmp := t.TempDir() | |
| 141 | - | home := makeFakeZotHome(t) | |
| 141 | + | home := makeFakeZutHome(t) | |
| 142 | 142 | ||
| 143 | 143 | // Set up a remote and clone it as the "installed" extension. | |
| 144 | 144 | remote := filepath.Join(tmp, "remote.git") | |
| @@ -165,7 +165,7 @@ func TestUpdateOneExtension_UpToDate(t *testing.T) { | |||
| 165 | 165 | func TestUpdateOneExtension_PullsNewCommit(t *testing.T) { | |
| 166 | 166 | gitAvailable(t) | |
| 167 | 167 | tmp := t.TempDir() | |
| 168 | - | home := makeFakeZotHome(t) | |
| 168 | + | home := makeFakeZutHome(t) | |
| 169 | 169 | ||
| 170 | 170 | remote := filepath.Join(tmp, "remote.git") | |
| 171 | 171 | initBareRepo(t, remote) | |
| @@ -201,7 +201,7 @@ func TestUpdateOneExtension_PullsNewCommit(t *testing.T) { | |||
| 201 | 201 | func TestUpdateOneExtension_StashesDirtyWorktree(t *testing.T) { | |
| 202 | 202 | gitAvailable(t) | |
| 203 | 203 | tmp := t.TempDir() | |
| 204 | - | home := makeFakeZotHome(t) | |
| 204 | + | home := makeFakeZutHome(t) | |
| 205 | 205 | ||
| 206 | 206 | remote := filepath.Join(tmp, "remote.git") | |
| 207 | 207 | initBareRepo(t, remote) | |
| @@ -244,7 +244,7 @@ func TestUpdateOneExtension_StashesDirtyWorktree(t *testing.T) { | |||
| 244 | 244 | func TestUpdateOneExtension_DivergedFails(t *testing.T) { | |
| 245 | 245 | gitAvailable(t) | |
| 246 | 246 | tmp := t.TempDir() | |
| 247 | - | home := makeFakeZotHome(t) | |
| 247 | + | home := makeFakeZutHome(t) | |
| 248 | 248 | ||
| 249 | 249 | remote := filepath.Join(tmp, "remote.git") | |
| 250 | 250 | initBareRepo(t, remote) | |
| @@ -281,7 +281,7 @@ func TestUpdateOneExtension_DivergedFails(t *testing.T) { | |||
| 281 | 281 | func TestUpdateOneExtension_BadRemoteFails(t *testing.T) { | |
| 282 | 282 | gitAvailable(t) | |
| 283 | 283 | tmp := t.TempDir() | |
| 284 | - | home := makeFakeZotHome(t) | |
| 284 | + | home := makeFakeZutHome(t) | |
| 285 | 285 | ||
| 286 | 286 | // Init a repo with an "origin" that points at a non-existent path. | |
| 287 | 287 | extDir := filepath.Join(home, "extensions", "bad") | |
| @@ -312,7 +312,7 @@ func TestUpdateOneExtension_BadRemoteFails(t *testing.T) { | |||
| 312 | 312 | func TestUpdateAllExtensions_MixedSet(t *testing.T) { | |
| 313 | 313 | gitAvailable(t) | |
| 314 | 314 | tmp := t.TempDir() | |
| 315 | - | home := makeFakeZotHome(t) | |
| 315 | + | home := makeFakeZutHome(t) | |
| 316 | 316 | ||
| 317 | 317 | // 1 plain non-git extension (skipped) | |
| 318 | 318 | extWithManifest(t, home, "plain", "") | |
M packages/agent/fast_mode_test.go +7 -7
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "testing" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | func TestParseArgsFastModeChildFlag(t *testing.T) { | |
| @@ -23,7 +23,7 @@ func TestParseArgsFastModeChildFlag(t *testing.T) { | |||
| 23 | 23 | } | |
| 24 | 24 | ||
| 25 | 25 | func TestResolveFastModeDefaultsOff(t *testing.T) { | |
| 26 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 26 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 27 | 27 | if err := SaveConfig(Config{Provider: "openai", Model: "gpt-5"}); err != nil { | |
| 28 | 28 | t.Fatal(err) | |
| 29 | 29 | } | |
| @@ -38,7 +38,7 @@ func TestResolveFastModeDefaultsOff(t *testing.T) { | |||
| 38 | 38 | } | |
| 39 | 39 | ||
| 40 | 40 | func TestResolveFastModeFromConfig(t *testing.T) { | |
| 41 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 41 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 42 | 42 | enabled := true | |
| 43 | 43 | if err := SaveConfig(Config{Provider: "openai", Model: "gpt-5", FastMode: &enabled}); err != nil { | |
| 44 | 44 | t.Fatal(err) | |
| @@ -54,7 +54,7 @@ func TestResolveFastModeFromConfig(t *testing.T) { | |||
| 54 | 54 | } | |
| 55 | 55 | ||
| 56 | 56 | func TestResolveFastModeRemainsEnabledForUnsupportedProviderUntilRequest(t *testing.T) { | |
| 57 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 57 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 58 | 58 | enabled := true | |
| 59 | 59 | if err := SaveConfig(Config{Provider: "anthropic", Model: "claude-sonnet-4-5", FastMode: &enabled}); err != nil { | |
| 60 | 60 | t.Fatal(err) | |
| @@ -70,7 +70,7 @@ func TestResolveFastModeRemainsEnabledForUnsupportedProviderUntilRequest(t *test | |||
| 70 | 70 | } | |
| 71 | 71 | ||
| 72 | 72 | func TestResolveFastModeRequiresExplicitChildOverride(t *testing.T) { | |
| 73 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 73 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 74 | 74 | if err := SaveConfig(Config{Provider: "openai", Model: "gpt-5"}); err != nil { | |
| 75 | 75 | t.Fatal(err) | |
| 76 | 76 | } | |
| @@ -85,7 +85,7 @@ func TestResolveFastModeRequiresExplicitChildOverride(t *testing.T) { | |||
| 85 | 85 | } | |
| 86 | 86 | ||
| 87 | 87 | func TestResolveFastModeExplicitChildDisableOverridesConfig(t *testing.T) { | |
| 88 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 88 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 89 | 89 | enabled := true | |
| 90 | 90 | if err := SaveConfig(Config{Provider: "openai", Model: "gpt-5", FastMode: &enabled}); err != nil { | |
| 91 | 91 | t.Fatal(err) | |
| @@ -105,7 +105,7 @@ func TestResolveFastModeExplicitChildDisableOverridesConfig(t *testing.T) { | |||
| 105 | 105 | } | |
| 106 | 106 | ||
| 107 | 107 | func TestConfigSettingsStorePersistsFastMode(t *testing.T) { | |
| 108 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 108 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 109 | 109 | if err := SaveConfig(Config{Theme: "dark"}); err != nil { | |
| 110 | 110 | t.Fatal(err) | |
| 111 | 111 | } | |
M packages/agent/instruction_context.go +2 -2
| @@ -3,8 +3,8 @@ package agent | |||
| 3 | 3 | import ( | |
| 4 | 4 | "sort" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 7 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 6 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 7 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | // instructionContextPaths returns the loaded instruction paths in effective | |
M packages/agent/instruction_context_test.go +2 -2
| @@ -4,8 +4,8 @@ import ( | |||
| 4 | 4 | "reflect" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 8 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 7 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 8 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | func TestInstructionContextPathsPreserveLoadOrder(t *testing.T) { | |
M packages/agent/lsp/client.go +2 -2
| @@ -164,7 +164,7 @@ func (c *Client) Initialize(ctx context.Context) error { | |||
| 164 | 164 | }, | |
| 165 | 165 | }, | |
| 166 | 166 | "initializationOptions": c.options.InitializationOptions, | |
| 167 | - | "clientInfo": map[string]any{"name": "zot", "version": "lsp"}, | |
| 167 | + | "clientInfo": map[string]any{"name": "zut", "version": "lsp"}, | |
| 168 | 168 | } | |
| 169 | 169 | result, err := c.Request(ctx, "initialize", params) | |
| 170 | 170 | if err != nil { | |
| @@ -273,7 +273,7 @@ func (c *Client) DidOpen(path, languageID, text string) error { | |||
| 273 | 273 | } | |
| 274 | 274 | ||
| 275 | 275 | // DidChange sends a full-content change, which avoids version/range drift | |
| 276 | - | // between zot and servers that use different incremental-sync policies. | |
| 276 | + | // between zut and servers that use different incremental-sync policies. | |
| 277 | 277 | func (c *Client) DidChange(path, text string) error { | |
| 278 | 278 | uri, err := pathToURI(path) | |
| 279 | 279 | if err != nil { | |
M packages/agent/lsp/config.go +10 -10
| @@ -64,14 +64,14 @@ func BuiltinServers() []ServerConfig { | |||
| 64 | 64 | ||
| 65 | 65 | // LoadConfig reads the global and project lsp.json files. Project files are | |
| 66 | 66 | // applied after the global file; within a project, lsp.json, .lsp.json, and | |
| 67 | - | // .zot/lsp.json are applied in that order. Missing files are harmless. | |
| 67 | + | // .zut/lsp.json are applied in that order. Missing files are harmless. | |
| 68 | 68 | func LoadConfig(cwd string) (Config, error) { | |
| 69 | 69 | cwd, err := absDir(cwd) | |
| 70 | 70 | if err != nil { | |
| 71 | 71 | return Config{}, err | |
| 72 | 72 | } | |
| 73 | 73 | var files []string | |
| 74 | - | if home := zotHome(); home != "" { | |
| 74 | + | if home := zutHome(); home != "" { | |
| 75 | 75 | files = append(files, filepath.Join(home, "lsp.json")) | |
| 76 | 76 | } | |
| 77 | 77 | files = append(files, | |
| @@ -79,7 +79,7 @@ func LoadConfig(cwd string) (Config, error) { | |||
| 79 | 79 | filepath.Join(cwd, ".omp", "lsp.json"), | |
| 80 | 80 | filepath.Join(cwd, "lsp.json"), | |
| 81 | 81 | filepath.Join(cwd, ".lsp.json"), | |
| 82 | - | filepath.Join(cwd, ".zot", "lsp.json"), | |
| 82 | + | filepath.Join(cwd, ".zut", "lsp.json"), | |
| 83 | 83 | ) | |
| 84 | 84 | ||
| 85 | 85 | objects := make(map[string]map[string]json.RawMessage) | |
| @@ -341,12 +341,12 @@ func absDir(cwd string) (string, error) { | |||
| 341 | 341 | return filepath.Clean(abs), nil | |
| 342 | 342 | } | |
| 343 | 343 | ||
| 344 | - | func zotHome() string { | |
| 345 | - | if value := strings.TrimSpace(os.Getenv("ZOT_HOME")); value != "" { | |
| 344 | + | func zutHome() string { | |
| 345 | + | if value := strings.TrimSpace(os.Getenv("ZUT_HOME")); value != "" { | |
| 346 | 346 | return value | |
| 347 | 347 | } | |
| 348 | 348 | if value := strings.TrimSpace(os.Getenv("XDG_STATE_HOME")); value != "" { | |
| 349 | - | return filepath.Join(value, "zot") | |
| 349 | + | return filepath.Join(value, "zut") | |
| 350 | 350 | } | |
| 351 | 351 | switch runtime.GOOS { | |
| 352 | 352 | case "darwin": | |
| @@ -354,17 +354,17 @@ func zotHome() string { | |||
| 354 | 354 | if home == "" { | |
| 355 | 355 | return "" | |
| 356 | 356 | } | |
| 357 | - | return filepath.Join(home, "Library", "Application Support", "zot") | |
| 357 | + | return filepath.Join(home, "Library", "Application Support", "zut") | |
| 358 | 358 | case "windows": | |
| 359 | 359 | if value := strings.TrimSpace(os.Getenv("LOCALAPPDATA")); value != "" { | |
| 360 | - | return filepath.Join(value, "zot") | |
| 360 | + | return filepath.Join(value, "zut") | |
| 361 | 361 | } | |
| 362 | 362 | } | |
| 363 | 363 | home, _ := os.UserHomeDir() | |
| 364 | 364 | if home == "" { | |
| 365 | 365 | return "" | |
| 366 | 366 | } | |
| 367 | - | return filepath.Join(home, ".local", "state", "zot") | |
| 367 | + | return filepath.Join(home, ".local", "state", "zut") | |
| 368 | 368 | } | |
| 369 | 369 | ||
| 370 | 370 | func providerIDs(data []byte) map[string]struct{} { | |
| @@ -480,7 +480,7 @@ func strconvQuote(s string) string { | |||
| 480 | 480 | } | |
| 481 | 481 | ||
| 482 | 482 | // flattenProviderObject adapts nested provider shapes to the flat | |
| 483 | - | // ServerConfig fields used by zot. Unknown provider metadata remains | |
| 483 | + | // ServerConfig fields used by zut. Unknown provider metadata remains | |
| 484 | 484 | // harmless JSON and is ignored by decodeServer. | |
| 485 | 485 | func flattenProviderObject(object map[string]json.RawMessage) { | |
| 486 | 486 | if _, exists := object["command"]; !exists { | |
M packages/agent/lsp/edits.go +1 -1
| @@ -187,7 +187,7 @@ func prepareTextEdits(path string, edits []TextEdit) ([]byte, os.FileMode, error | |||
| 187 | 187 | } | |
| 188 | 188 | ||
| 189 | 189 | func writeTextEdits(path string, out []byte, mode os.FileMode) error { | |
| 190 | - | tmp, err := os.CreateTemp(filepath.Dir(path), ".zot-lsp-edit-*") | |
| 190 | + | tmp, err := os.CreateTemp(filepath.Dir(path), ".zut-lsp-edit-*") | |
| 191 | 191 | if err != nil { | |
| 192 | 192 | return err | |
| 193 | 193 | } | |
M packages/agent/lsp/lsp_test.go +16 -16
| @@ -16,7 +16,7 @@ import ( | |||
| 16 | 16 | ) | |
| 17 | 17 | ||
| 18 | 18 | func TestFakeLSPProcess(t *testing.T) { | |
| 19 | - | if os.Getenv("ZOT_FAKE_LSP") != "1" { | |
| 19 | + | if os.Getenv("ZUT_FAKE_LSP") != "1" { | |
| 20 | 20 | return | |
| 21 | 21 | } | |
| 22 | 22 | reader := bufio.NewReader(os.Stdin) | |
| @@ -56,7 +56,7 @@ func TestFakeLSPProcess(t *testing.T) { | |||
| 56 | 56 | func TestClientFakeStdioRPC(t *testing.T) { | |
| 57 | 57 | root := t.TempDir() | |
| 58 | 58 | script := os.Args[0] | |
| 59 | - | spec := ServerConfig{ID: "fake", Kind: "lsp", Command: script, Args: []string{"-test.run=TestFakeLSPProcess"}, Env: map[string]string{"ZOT_FAKE_LSP": "1"}} | |
| 59 | + | spec := ServerConfig{ID: "fake", Kind: "lsp", Command: script, Args: []string{"-test.run=TestFakeLSPProcess"}, Env: map[string]string{"ZUT_FAKE_LSP": "1"}} | |
| 60 | 60 | diagnostics := make(chan []Diagnostic, 1) | |
| 61 | 61 | client, err := NewClientWithContext(context.Background(), spec, root, ClientOptions{OnDiagnostics: func(value []Diagnostic) { diagnostics <- value }}) | |
| 62 | 62 | if err != nil { | |
| @@ -157,25 +157,25 @@ func TestFramingRoundTripWithAdditionalHeader(t *testing.T) { | |||
| 157 | 157 | } | |
| 158 | 158 | } | |
| 159 | 159 | ||
| 160 | - | func TestZotHomeEnvironmentPrecedence(t *testing.T) { | |
| 161 | - | configuredHome := filepath.Join(t.TempDir(), "zot") | |
| 160 | + | func TestZutHomeEnvironmentPrecedence(t *testing.T) { | |
| 161 | + | configuredHome := filepath.Join(t.TempDir(), "zut") | |
| 162 | 162 | xdgHome := filepath.Join(t.TempDir(), "state") | |
| 163 | - | t.Setenv("ZOT_HOME", configuredHome) | |
| 163 | + | t.Setenv("ZUT_HOME", configuredHome) | |
| 164 | 164 | t.Setenv("XDG_STATE_HOME", xdgHome) | |
| 165 | - | if got := zotHome(); got != configuredHome { | |
| 166 | - | t.Fatalf("ZOT_HOME path = %q, want %q", got, configuredHome) | |
| 165 | + | if got := zutHome(); got != configuredHome { | |
| 166 | + | t.Fatalf("ZUT_HOME path = %q, want %q", got, configuredHome) | |
| 167 | 167 | } | |
| 168 | - | t.Setenv("ZOT_HOME", "") | |
| 169 | - | if got := zotHome(); got != filepath.Join(xdgHome, "zot") { | |
| 170 | - | t.Fatalf("XDG_STATE_HOME path = %q, want %q", got, filepath.Join(xdgHome, "zot")) | |
| 168 | + | t.Setenv("ZUT_HOME", "") | |
| 169 | + | if got := zutHome(); got != filepath.Join(xdgHome, "zut") { | |
| 170 | + | t.Fatalf("XDG_STATE_HOME path = %q, want %q", got, filepath.Join(xdgHome, "zut")) | |
| 171 | 171 | } | |
| 172 | 172 | } | |
| 173 | 173 | ||
| 174 | 174 | func TestLoadConfigMergesGlobalProjectAndBuiltins(t *testing.T) { | |
| 175 | 175 | root := t.TempDir() | |
| 176 | - | zotHome := t.TempDir() | |
| 177 | - | t.Setenv("ZOT_HOME", zotHome) | |
| 178 | - | if err := os.WriteFile(filepath.Join(zotHome, "lsp.json"), []byte(`{"servers":{"gopls":{"settings":{"global":true},"args":["--global"]}}}`), 0o644); err != nil { | |
| 176 | + | zutHome := t.TempDir() | |
| 177 | + | t.Setenv("ZUT_HOME", zutHome) | |
| 178 | + | if err := os.WriteFile(filepath.Join(zutHome, "lsp.json"), []byte(`{"servers":{"gopls":{"settings":{"global":true},"args":["--global"]}}}`), 0o644); err != nil { | |
| 179 | 179 | t.Fatal(err) | |
| 180 | 180 | } | |
| 181 | 181 | if err := os.WriteFile(filepath.Join(root, "go.mod"), []byte("module example\n"), 0o644); err != nil { | |
| @@ -214,8 +214,8 @@ func TestLoadConfigMergesGlobalProjectAndBuiltins(t *testing.T) { | |||
| 214 | 214 | ||
| 215 | 215 | func TestLoadConfigAcceptsProviderArrays(t *testing.T) { | |
| 216 | 216 | root := t.TempDir() | |
| 217 | - | zotHome := t.TempDir() | |
| 218 | - | t.Setenv("ZOT_HOME", zotHome) | |
| 217 | + | zutHome := t.TempDir() | |
| 218 | + | t.Setenv("ZUT_HOME", zutHome) | |
| 219 | 219 | if err := os.WriteFile(filepath.Join(root, "go.mod"), []byte("module example\n"), 0o644); err != nil { | |
| 220 | 220 | t.Fatal(err) | |
| 221 | 221 | } | |
| @@ -245,7 +245,7 @@ func TestLoadConfigAcceptsProviderArrays(t *testing.T) { | |||
| 245 | 245 | ||
| 246 | 246 | func TestLoadConfigAutoDetectFalseKeepsExplicitProviders(t *testing.T) { | |
| 247 | 247 | root := t.TempDir() | |
| 248 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 248 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 249 | 249 | if err := os.WriteFile(filepath.Join(root, "go.mod"), []byte("module example\n"), 0o644); err != nil { | |
| 250 | 250 | t.Fatal(err) | |
| 251 | 251 | } | |
M packages/agent/lsp/protocol.go +1 -1
| @@ -26,7 +26,7 @@ type TextEdit struct { | |||
| 26 | 26 | NewText string `json:"newText"` | |
| 27 | 27 | } | |
| 28 | 28 | ||
| 29 | - | // WorkspaceEdit is the safe subset of a workspace edit understood by zot. | |
| 29 | + | // WorkspaceEdit is the safe subset of a workspace edit understood by zut. | |
| 30 | 30 | // Resource operations in DocumentChanges are deliberately rejected. | |
| 31 | 31 | type WorkspaceEdit struct { | |
| 32 | 32 | Changes map[string][]TextEdit `json:"changes,omitempty"` | |
M packages/agent/modelsync.go +13 -13
| @@ -8,17 +8,17 @@ import ( | |||
| 8 | 8 | "strings" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // ModelCachePath returns the on-disk location of the merged model cache. | |
| 15 | 15 | func ModelCachePath() string { | |
| 16 | - | return filepath.Join(ZotHome(), "models-cache.json") | |
| 16 | + | return filepath.Join(ZutHome(), "models-cache.json") | |
| 17 | 17 | } | |
| 18 | 18 | ||
| 19 | 19 | // UserModelsPath returns the path to the user's models.json override. | |
| 20 | 20 | func UserModelsPath() string { | |
| 21 | - | return filepath.Join(ZotHome(), "models.json") | |
| 21 | + | return filepath.Join(ZutHome(), "models.json") | |
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | 24 | // LoadCachedModels loads the cache file and applies it to the provider | |
| @@ -34,7 +34,7 @@ func LoadCachedModels() { | |||
| 34 | 34 | } | |
| 35 | 35 | } | |
| 36 | 36 | ||
| 37 | - | // LoadUserModels reads $ZOT_HOME/models.json and merges any user-defined | |
| 37 | + | // LoadUserModels reads $ZUT_HOME/models.json and merges any user-defined | |
| 38 | 38 | // models into the active catalog. User models take highest precedence. | |
| 39 | 39 | // Any validation issues (bad provider id, empty model id, malformed | |
| 40 | 40 | // JSON, negative widths) are surfaced as one warning per line on stderr; | |
| @@ -42,14 +42,14 @@ func LoadCachedModels() { | |||
| 42 | 42 | func LoadUserModels() { | |
| 43 | 43 | models, warnings := provider.LoadUserModelsWithWarnings(UserModelsPath()) | |
| 44 | 44 | for _, w := range warnings { | |
| 45 | - | fmt.Fprintln(os.Stderr, "zot:", w) | |
| 45 | + | fmt.Fprintln(os.Stderr, "zut:", w) | |
| 46 | 46 | } | |
| 47 | 47 | provider.SetUserModels(models) | |
| 48 | 48 | } | |
| 49 | 49 | ||
| 50 | 50 | // isGatewayProvider returns true for providers whose OpenAI-compatible | |
| 51 | - | // endpoint can accept routed model IDs that are not present in zot's local | |
| 52 | - | // catalog. Vercel AI Gateway is intentionally not listed here: zot currently | |
| 51 | + | // endpoint can accept routed model IDs that are not present in zut's local | |
| 52 | + | // catalog. Vercel AI Gateway is intentionally not listed here: zut currently | |
| 53 | 53 | // talks to it through the Anthropic-compatible client, which still requires | |
| 54 | 54 | // catalog metadata for request shaping. | |
| 55 | 55 | func isGatewayProvider(prov string) bool { | |
| @@ -81,20 +81,20 @@ func isGatewayRoutedModelID(model string) bool { | |||
| 81 | 81 | // half-applied switch) -> reset model to the provider's default. | |
| 82 | 82 | // | |
| 83 | 83 | // Gateway providers are exempt from the cross-provider model check for routed | |
| 84 | - | // model IDs because those IDs can be valid even when absent from zot's catalog. | |
| 84 | + | // model IDs because those IDs can be valid even when absent from zut's catalog. | |
| 85 | 85 | // | |
| 86 | 86 | // Silent on success; one stderr line per repair. Errors loading or | |
| 87 | 87 | // saving the file are non-fatal — the caller continues with defaults. | |
| 88 | 88 | func ValidateAndRepairConfig() { | |
| 89 | 89 | cfg, err := LoadConfig() | |
| 90 | 90 | if err != nil { | |
| 91 | - | fmt.Fprintf(os.Stderr, "zot: config.json: %v (using defaults)\n", err) | |
| 91 | + | fmt.Fprintf(os.Stderr, "zut: config.json: %v (using defaults)\n", err) | |
| 92 | 92 | return | |
| 93 | 93 | } | |
| 94 | 94 | changed := false | |
| 95 | 95 | ||
| 96 | 96 | if cfg.Provider != "" && !isKnownProvider(cfg.Provider) { | |
| 97 | - | fmt.Fprintf(os.Stderr, "zot: config.json: unknown provider %q reset to \"anthropic\"\n", cfg.Provider) | |
| 97 | + | fmt.Fprintf(os.Stderr, "zut: config.json: unknown provider %q reset to \"anthropic\"\n", cfg.Provider) | |
| 98 | 98 | cfg.Provider = "anthropic" | |
| 99 | 99 | cfg.Model = "" | |
| 100 | 100 | changed = true | |
| @@ -110,7 +110,7 @@ func ValidateAndRepairConfig() { | |||
| 110 | 110 | } else if m, err := provider.FindModel("", cfg.Model); err == nil { | |
| 111 | 111 | fix := defaultModelForProvider(cfg.Provider) | |
| 112 | 112 | fmt.Fprintf(os.Stderr, | |
| 113 | - | "zot: config.json: model %q belongs to provider %q (config has provider=%q); switched model to %q\n", | |
| 113 | + | "zut: config.json: model %q belongs to provider %q (config has provider=%q); switched model to %q\n", | |
| 114 | 114 | cfg.Model, m.Provider, cfg.Provider, fix) | |
| 115 | 115 | cfg.Model = fix | |
| 116 | 116 | changed = true | |
| @@ -118,7 +118,7 @@ func ValidateAndRepairConfig() { | |||
| 118 | 118 | // Model id not in any catalog. Reset to provider's default. | |
| 119 | 119 | fix := defaultModelForProvider(cfg.Provider) | |
| 120 | 120 | fmt.Fprintf(os.Stderr, | |
| 121 | - | "zot: config.json: model %q not found in the active catalog; switched to %q\n", | |
| 121 | + | "zut: config.json: model %q not found in the active catalog; switched to %q\n", | |
| 122 | 122 | cfg.Model, fix) | |
| 123 | 123 | cfg.Model = fix | |
| 124 | 124 | changed = true | |
| @@ -128,7 +128,7 @@ func ValidateAndRepairConfig() { | |||
| 128 | 128 | ||
| 129 | 129 | if changed { | |
| 130 | 130 | if err := SaveConfig(cfg); err != nil { | |
| 131 | - | fmt.Fprintf(os.Stderr, "zot: config.json: failed to persist repair: %v\n", err) | |
| 131 | + | fmt.Fprintf(os.Stderr, "zut: config.json: failed to persist repair: %v\n", err) | |
| 132 | 132 | } | |
| 133 | 133 | } | |
| 134 | 134 | } | |
M packages/agent/modelsync_test.go +9 -9
| @@ -10,7 +10,7 @@ import ( | |||
| 10 | 10 | "path/filepath" | |
| 11 | 11 | "testing" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | func TestRefreshLlamaCPPModelsAddsOnlyLoadedModels(t *testing.T) { | |
| @@ -23,7 +23,7 @@ func TestRefreshLlamaCPPModelsAddsOnlyLoadedModels(t *testing.T) { | |||
| 23 | 23 | })) | |
| 24 | 24 | defer server.Close() | |
| 25 | 25 | ||
| 26 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 26 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 27 | 27 | t.Setenv("LLAMA_BASE_URL", "") | |
| 28 | 28 | if err := AuthStoreFor().SetEndpointCredential(provider.LlamaCPPProviderID, server.URL, ""); err != nil { | |
| 29 | 29 | t.Fatal(err) | |
| @@ -52,7 +52,7 @@ func TestRefreshLlamaCPPModelsAddsOnlyLoadedModels(t *testing.T) { | |||
| 52 | 52 | // model to anthropic's default and persist. | |
| 53 | 53 | func TestValidateAndRepairConfig_MismatchedPair(t *testing.T) { | |
| 54 | 54 | home := t.TempDir() | |
| 55 | - | t.Setenv("ZOT_HOME", home) | |
| 55 | + | t.Setenv("ZUT_HOME", home) | |
| 56 | 56 | ||
| 57 | 57 | must := func(c Config) { | |
| 58 | 58 | t.Helper() | |
| @@ -85,7 +85,7 @@ func TestValidateAndRepairConfig_MismatchedPair(t *testing.T) { | |||
| 85 | 85 | // (e.g. user removed it from a previous build). | |
| 86 | 86 | func TestValidateAndRepairConfig_UnknownProvider(t *testing.T) { | |
| 87 | 87 | home := t.TempDir() | |
| 88 | - | t.Setenv("ZOT_HOME", home) | |
| 88 | + | t.Setenv("ZUT_HOME", home) | |
| 89 | 89 | ||
| 90 | 90 | b, _ := json.Marshal(Config{Provider: "made-up-provider", Model: "some-model"}) | |
| 91 | 91 | _ = os.WriteFile(filepath.Join(home, "config.json"), b, 0o644) | |
| @@ -106,7 +106,7 @@ func TestValidateAndRepairConfig_UnknownProvider(t *testing.T) { | |||
| 106 | 106 | // longer in the catalog. | |
| 107 | 107 | func TestValidateAndRepairConfig_UnknownModel(t *testing.T) { | |
| 108 | 108 | home := t.TempDir() | |
| 109 | - | t.Setenv("ZOT_HOME", home) | |
| 109 | + | t.Setenv("ZUT_HOME", home) | |
| 110 | 110 | ||
| 111 | 111 | b, _ := json.Marshal(Config{Provider: "anthropic", Model: "claude-deleted-model"}) | |
| 112 | 112 | _ = os.WriteFile(filepath.Join(home, "config.json"), b, 0o644) | |
| @@ -124,7 +124,7 @@ func TestValidateAndRepairConfig_UnknownModel(t *testing.T) { | |||
| 124 | 124 | ||
| 125 | 125 | func TestValidateAndRepairConfig_DuplicateModelIDValidForConfiguredProvider(t *testing.T) { | |
| 126 | 126 | home := t.TempDir() | |
| 127 | - | t.Setenv("ZOT_HOME", home) | |
| 127 | + | t.Setenv("ZUT_HOME", home) | |
| 128 | 128 | ||
| 129 | 129 | b, _ := json.Marshal(Config{Provider: "openai-codex", Model: "gpt-5.5"}) | |
| 130 | 130 | _ = os.WriteFile(filepath.Join(home, "config.json"), b, 0o644) | |
| @@ -142,7 +142,7 @@ func TestValidateAndRepairConfig_DuplicateModelIDValidForConfiguredProvider(t *t | |||
| 142 | 142 | ||
| 143 | 143 | func TestValidateAndRepairConfig_OpenRouterPreservesRoutedModelID(t *testing.T) { | |
| 144 | 144 | home := t.TempDir() | |
| 145 | - | t.Setenv("ZOT_HOME", home) | |
| 145 | + | t.Setenv("ZUT_HOME", home) | |
| 146 | 146 | ||
| 147 | 147 | want := "deepseek/deepseek-v4-flash" | |
| 148 | 148 | b, _ := json.Marshal(Config{Provider: "openrouter", Model: want}) | |
| @@ -161,7 +161,7 @@ func TestValidateAndRepairConfig_OpenRouterPreservesRoutedModelID(t *testing.T) | |||
| 161 | 161 | ||
| 162 | 162 | func TestValidateAndRepairConfig_GatewayPlainUnknownModelStillRepairs(t *testing.T) { | |
| 163 | 163 | home := t.TempDir() | |
| 164 | - | t.Setenv("ZOT_HOME", home) | |
| 164 | + | t.Setenv("ZUT_HOME", home) | |
| 165 | 165 | ||
| 166 | 166 | b, _ := json.Marshal(Config{Provider: "openrouter", Model: "not-a-routed-model"}) | |
| 167 | 167 | _ = os.WriteFile(filepath.Join(home, "config.json"), b, 0o644) | |
| @@ -180,7 +180,7 @@ func TestValidateAndRepairConfig_GatewayPlainUnknownModelStillRepairs(t *testing | |||
| 180 | 180 | // TestValidateAndRepairConfig_HappyPath leaves a valid config alone. | |
| 181 | 181 | func TestValidateAndRepairConfig_HappyPath(t *testing.T) { | |
| 182 | 182 | home := t.TempDir() | |
| 183 | - | t.Setenv("ZOT_HOME", home) | |
| 183 | + | t.Setenv("ZUT_HOME", home) | |
| 184 | 184 | ||
| 185 | 185 | b, _ := json.Marshal(Config{Provider: "anthropic", Model: "claude-sonnet-4-5"}) | |
| 186 | 186 | _ = os.WriteFile(filepath.Join(home, "config.json"), b, 0o644) | |
M packages/agent/modes/alert_test.go +4 -4
| @@ -8,10 +8,10 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 12 | - | "github.com/patriceckhart/zot/packages/core" | |
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 14 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 11 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 12 | + | "github.com/bnema/zut/packages/core" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | + | "github.com/bnema/zut/packages/tui" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | type alertTestTerminal struct { | |
M packages/agent/modes/auto_compact_test.go +1 -1
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 6 | + | "github.com/bnema/zut/packages/provider" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestNormalizeAutoCompactThreshold(t *testing.T) { | |
M packages/agent/modes/auto_subagents_policy_test.go +3 -3
| @@ -6,9 +6,9 @@ import ( | |||
| 6 | 6 | "testing" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | func TestAutoSubagentsSettingsDisableUnavailablePolicy(t *testing.T) { | |
M packages/agent/modes/auto_subagents_test.go +3 -3
| @@ -8,9 +8,9 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 12 | - | "github.com/patriceckhart/zot/packages/core" | |
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 12 | + | "github.com/bnema/zut/packages/core" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | func TestAutoSubagentsSystemPromptTogglesProfileManifestWithDelegationGuidance(t *testing.T) { | |
M packages/agent/modes/bot/adapter.go +1 -1
| @@ -7,7 +7,7 @@ package bot | |||
| 7 | 7 | import ( | |
| 8 | 8 | "context" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // InboundMessage is a protocol-normalised message from a user. | |
M packages/agent/modes/bot/runner.go +4 -4
| @@ -8,8 +8,8 @@ import ( | |||
| 8 | 8 | "strings" | |
| 9 | 9 | "sync" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/core" | |
| 12 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/core" | |
| 12 | + | "github.com/bnema/zut/packages/provider" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | // stderr is a tiny hook so tests can redirect bot logging. | |
| @@ -17,7 +17,7 @@ var stderr = func() io.Writer { return os.Stderr } | |||
| 17 | 17 | ||
| 18 | 18 | // Config holds runner-level settings that are protocol-independent. | |
| 19 | 19 | type Config struct { | |
| 20 | - | ZotHome string | |
| 20 | + | ZutHome string | |
| 21 | 21 | Provider string | |
| 22 | 22 | Model string | |
| 23 | 23 | AuthMethod string | |
| @@ -107,7 +107,7 @@ func (r *Runner) handleCommand(cmd Command, msg InboundMessage) { | |||
| 107 | 107 | switch cmd { | |
| 108 | 108 | case CmdStart, CmdHelp: | |
| 109 | 109 | _ = r.adapter.Send(context.Background(), msg.ChannelID, | |
| 110 | - | "send me any message and i'll forward it to zot. attach an image and i'll pass it to the model. commands: /status, /stop, or plain stop.", | |
| 110 | + | "send me any message and i'll forward it to zut. attach an image and i'll pass it to the model. commands: /status, /stop, or plain stop.", | |
| 111 | 111 | SendOptions{ReplyToMessageID: msg.MessageID}) | |
| 112 | 112 | case CmdStatus: | |
| 113 | 113 | r.sendStatus(msg.ChannelID, msg.MessageID) | |
M packages/agent/modes/bot/runner_test.go +2 -2
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "testing" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | type testAdapter struct{} | |
M packages/agent/modes/bot/status.go +1 -1
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | "os" | |
| 6 | 6 | "strings" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 8 | + | "github.com/bnema/zut/packages/provider" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // StatusSnapshot is the small cross-host state bundle rendered for | |
M packages/agent/modes/btw_dialog.go +3 -3
| @@ -6,9 +6,9 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "sync" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/tui" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // btwTurn is one user/assistant pair within a side chat. Kept | |
M packages/agent/modes/btw_dialog_test.go +3 -3
| @@ -9,9 +9,9 @@ import ( | |||
| 9 | 9 | "testing" | |
| 10 | 10 | "time" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/core" | |
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 14 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 12 | + | "github.com/bnema/zut/packages/core" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | + | "github.com/bnema/zut/packages/tui" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | type btwToolTestClient struct { | |
M packages/agent/modes/changelog_dialog.go +3 -3
| @@ -4,11 +4,11 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | "strings" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | // changelogDialog is the one-shot release-notes overlay shown the | |
| 11 | - | // first time a user launches a new zot version. The body is the | |
| 11 | + | // first time a user launches a new zut version. The body is the | |
| 12 | 12 | // markdown from the GitHub release page, rendered through the same | |
| 13 | 13 | // pipeline used for assistant messages so code fences + bold + | |
| 14 | 14 | // links look right. | |
| @@ -76,7 +76,7 @@ func (d *changelogDialog) Render(th tui.Theme, width int) []string { | |||
| 76 | 76 | if !d.Active() { | |
| 77 | 77 | return nil | |
| 78 | 78 | } | |
| 79 | - | title := fmt.Sprintf("zot %s \u2014 release notes (any key to dismiss)", d.version) | |
| 79 | + | title := fmt.Sprintf("zut %s \u2014 release notes (any key to dismiss)", d.version) | |
| 80 | 80 | out := []string{frameHeaderColor(th, title, width, th.Accent)} | |
| 81 | 81 | if d.url != "" { | |
| 82 | 82 | out = append(out, " "+th.FGColor(th.Muted, d.url)) | |
M packages/agent/modes/changelog_dialog_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestChangelogDialogWrapsReleaseNotes(t *testing.T) { | |
M packages/agent/modes/clipboard_images.go +1 -1
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "strings" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 6 | + | "github.com/bnema/zut/packages/provider" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | type clipboardImageAttachment struct { | |
M packages/agent/modes/clipboard_images_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 7 | + | "github.com/bnema/zut/packages/provider" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func testClipboardImage(marker string, data string) clipboardImageAttachment { | |
M packages/agent/modes/clipboard_inputs_test.go +1 -1
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 6 | + | "github.com/bnema/zut/packages/tui" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestFilterInputsAcceptPaste(t *testing.T) { | |
M packages/agent/modes/clipboard_paste.go +1 -1
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "strings" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 6 | + | "github.com/bnema/zut/packages/tui" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | type clipboardTextReader func() (string, bool, error) | |
M packages/agent/modes/clipboard_paste_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "errors" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestResolveClipboardTextConvertsClipboardKeyToPaste(t *testing.T) { | |
M packages/agent/modes/compact_queue_test.go +3 -3
| @@ -7,9 +7,9 @@ import ( | |||
| 7 | 7 | "testing" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 12 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | + | "github.com/bnema/zut/packages/tui" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | type compactQueueClient struct { | |
M packages/agent/modes/confirm_dialog.go +2 -2
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "sync" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // confirmRequest is one pending confirmation. The agent goroutine | |
M packages/agent/modes/confirm_dialog_test.go +4 -4
| @@ -8,10 +8,10 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 12 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 13 | - | "github.com/patriceckhart/zot/packages/core" | |
| 14 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 11 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 12 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 13 | + | "github.com/bnema/zut/packages/core" | |
| 14 | + | "github.com/bnema/zut/packages/tui" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | func TestConfirmToolCallAttachesDiffBeforeDecision(t *testing.T) { | |
M packages/agent/modes/dialog_frame.go +1 -1
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | ||
| 6 | 6 | "github.com/mattn/go-runewidth" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 8 | + | "github.com/bnema/zut/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // frameHeader returns a full-width rule with a small title at the left. | |
M packages/agent/modes/dialog_text.go +1 -1
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "strings" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 6 | + | "github.com/bnema/zut/packages/tui" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | const dialogTextIndent = " " | |
M packages/agent/modes/dialog_text_test.go +2 -2
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | ||
| 7 | 7 | "github.com/mattn/go-runewidth" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func assertRowsFitWidth(t *testing.T, rows []string, width int) { | |
M packages/agent/modes/double_escape.go +3 -3
| @@ -5,9 +5,9 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | "time" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/core" | |
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 8 | + | "github.com/bnema/zut/packages/core" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | const sessionTreeEscapeWindow = 500 * time.Millisecond | |
M packages/agent/modes/double_escape_test.go +3 -3
| @@ -5,9 +5,9 @@ import ( | |||
| 5 | 5 | "testing" | |
| 6 | 6 | "time" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/core" | |
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 8 | + | "github.com/bnema/zut/packages/core" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func TestDoubleEscapeTrackerWindowAndReset(t *testing.T) { | |
M packages/agent/modes/ext_notes_test.go +1 -1
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "testing" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/tui" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | func newNotesTestInteractive() *Interactive { | |
M packages/agent/modes/ext_panel_dialog.go +1 -1
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | ||
| 6 | 6 | "github.com/mattn/go-runewidth" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 8 | + | "github.com/bnema/zut/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | type extPanelDialog struct { | |
M packages/agent/modes/fast_mode_test.go +2 -2
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "testing" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | type recordingFastModeSettingsStore struct { | |
M packages/agent/modes/file_suggest.go +4 -4
| @@ -10,8 +10,8 @@ import ( | |||
| 10 | 10 | ||
| 11 | 11 | "github.com/sahilm/fuzzy" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/ignore" | |
| 14 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 13 | + | "github.com/bnema/zut/packages/ignore" | |
| 14 | + | "github.com/bnema/zut/packages/tui" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | // recursiveScanLimits bound the recursive walk so the picker stays | |
| @@ -63,7 +63,7 @@ type fileSuggester struct { | |||
| 63 | 63 | // cachedMTime is the mtime of cachedDir at the time of the scan. | |
| 64 | 64 | // scan() compares the current mtime against this on every call and | |
| 65 | 65 | // re-reads the directory if it has changed, so files or folders | |
| 66 | - | // created mid-session show up in the picker without restarting zot. | |
| 66 | + | // created mid-session show up in the picker without restarting zut. | |
| 67 | 67 | // Stat is cheap (single syscall) so doing it per keystroke while | |
| 68 | 68 | // the popup is open does not impact responsiveness. | |
| 69 | 69 | cachedMTime time.Time | |
| @@ -129,7 +129,7 @@ func (s *fileSuggester) browseDir() string { | |||
| 129 | 129 | // Results are cached by absolute path + mtime: a repeated call against | |
| 130 | 130 | // the same directory returns the cached slice when nothing has changed | |
| 131 | 131 | // on disk, and re-reads when an entry was added, removed, or renamed | |
| 132 | - | // (any of which bumps the directory's mtime on every filesystem zot | |
| 132 | + | // (any of which bumps the directory's mtime on every filesystem zut | |
| 133 | 133 | // supports). A failed stat falls through to a fresh ReadDir rather | |
| 134 | 134 | // than returning a stale cache so transient errors self-heal. | |
| 135 | 135 | func (s *fileSuggester) scan() []fileEntry { | |
M packages/agent/modes/help.go +2 -2
| @@ -4,8 +4,8 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | "strings" | |
| 6 | 6 | ||
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 7 | 8 | "github.com/mattn/go-runewidth" | |
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // helpKeyRows is the list of keybindings shown by /help. | |
| @@ -73,7 +73,7 @@ func renderHelpBlock(th tui.Theme, width int, llamaConfigured bool) []string { | |||
| 73 | 73 | } | |
| 74 | 74 | ||
| 75 | 75 | var out []string | |
| 76 | - | out = append(out, frameHeader(th, "zot help", width), "") | |
| 76 | + | out = append(out, frameHeader(th, "zut help", width), "") | |
| 77 | 77 | ||
| 78 | 78 | // commands section | |
| 79 | 79 | out = append(out, tui.Bold("slash commands:")) | |
M packages/agent/modes/help_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestHelpShowsLlamaOnlyWhenConfigured(t *testing.T) { | |
M packages/agent/modes/input_history_test.go +3 -3
| @@ -3,9 +3,9 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/core" | |
| 7 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 6 | + | "github.com/bnema/zut/packages/core" | |
| 7 | + | "github.com/bnema/zut/packages/provider" | |
| 8 | + | "github.com/bnema/zut/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | func TestInputHistoryUsesUpDown(t *testing.T) { | |
M packages/agent/modes/interactive.go +56 -56
| @@ -13,16 +13,16 @@ import ( | |||
| 13 | 13 | "sync" | |
| 14 | 14 | "time" | |
| 15 | 15 | ||
| 16 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 17 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 18 | - | "github.com/patriceckhart/zot/packages/agent/modes/telegram" | |
| 19 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 20 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 21 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 22 | - | "github.com/patriceckhart/zot/packages/core" | |
| 23 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 24 | - | "github.com/patriceckhart/zot/packages/provider/auth" | |
| 25 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 16 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 17 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 18 | + | "github.com/bnema/zut/packages/agent/modes/telegram" | |
| 19 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 20 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 21 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 22 | + | "github.com/bnema/zut/packages/core" | |
| 23 | + | "github.com/bnema/zut/packages/provider" | |
| 24 | + | "github.com/bnema/zut/packages/provider/auth" | |
| 25 | + | "github.com/bnema/zut/packages/tui" | |
| 26 | 26 | ) | |
| 27 | 27 | ||
| 28 | 28 | // InteractiveConfig configures the interactive loop. | |
| @@ -121,12 +121,12 @@ type InteractiveConfig struct { | |||
| 121 | 121 | ||
| 122 | 122 | // FlatTools renders tool calls without the bordered panel (a quiet | |
| 123 | 123 | // header line plus indented, frameless output). Mirrors the | |
| 124 | - | // resolved tool_render config / ZOT_FLAT_TOOLS env at startup. | |
| 124 | + | // resolved tool_render config / ZUT_FLAT_TOOLS env at startup. | |
| 125 | 125 | FlatTools bool | |
| 126 | 126 | ||
| 127 | 127 | // CompactUser renders sent user messages as a single quiet gutter | |
| 128 | 128 | // line instead of a padded, tinted bubble. Mirrors the resolved | |
| 129 | - | // compact_input config / ZOT_COMPACT_INPUT env at startup. | |
| 129 | + | // compact_input config / ZUT_COMPACT_INPUT env at startup. | |
| 130 | 130 | CompactUser bool | |
| 131 | 131 | ||
| 132 | 132 | // CompactMode mirrors the persisted compact_mode flag at startup. | |
| @@ -163,7 +163,7 @@ type InteractiveConfig struct { | |||
| 163 | 163 | SubagentsSystemAddendum string | |
| 164 | 164 | SettingsStore SettingsStore | |
| 165 | 165 | ||
| 166 | - | // Agent is optional. If nil, zot opens without credentials; the | |
| 166 | + | // Agent is optional. If nil, zut opens without credentials; the | |
| 167 | 167 | // user must /login before they can prompt. | |
| 168 | 168 | Agent *core.Agent | |
| 169 | 169 | ||
| @@ -209,8 +209,8 @@ type InteractiveConfig struct { | |||
| 209 | 209 | // the concrete provider/model in use. | |
| 210 | 210 | BuildAgent func() (*core.Agent, string, string, error) | |
| 211 | 211 | ||
| 212 | - | // SetKimiCLIFallbackDisabled controls whether zot may fall back to | |
| 213 | - | // the official Kimi Code CLI token when zot has no stored Kimi token. | |
| 212 | + | // SetKimiCLIFallbackDisabled controls whether zut may fall back to | |
| 213 | + | // the official Kimi Code CLI token when zut has no stored Kimi token. | |
| 214 | 214 | SetKimiCLIFallbackDisabled func(disabled bool) error | |
| 215 | 215 | ||
| 216 | 216 | // BuildAgentFor rebuilds the agent with an explicit provider/model | |
| @@ -232,13 +232,13 @@ type InteractiveConfig struct { | |||
| 232 | 232 | // picker to only show reachable models. | |
| 233 | 233 | LoggedInProviders func() []string | |
| 234 | 234 | ||
| 235 | - | // ZotHome is zot's global state directory, used by authentication, | |
| 235 | + | // ZutHome is zut's global state directory, used by authentication, | |
| 236 | 236 | // themes, extensions, and other shared configuration. | |
| 237 | - | ZotHome string | |
| 237 | + | ZutHome string | |
| 238 | 238 | ||
| 239 | 239 | // SessionsRoot is the root passed to core session operations. It differs | |
| 240 | - | // from ZotHome for Zotfile agents, whose sessions are isolated by agent | |
| 241 | - | // name. Empty falls back to ZotHome for embedders and tests. | |
| 240 | + | // from ZutHome for Zutfile agents, whose sessions are isolated by agent | |
| 241 | + | // name. Empty falls back to ZutHome for embedders and tests. | |
| 242 | 242 | SessionsRoot string | |
| 243 | 243 | ||
| 244 | 244 | // Version is the binary's current version (from main.version). | |
| @@ -260,7 +260,7 @@ type InteractiveConfig struct { | |||
| 260 | 260 | // callback returns the new agent message slice so the TUI can invalidate. | |
| 261 | 261 | LoadSession func(path string) error | |
| 262 | 262 | ||
| 263 | - | // ChangeCWD switches the running zot session's working directory | |
| 263 | + | // ChangeCWD switches the running zut session's working directory | |
| 264 | 264 | // to path. The host closes the current session, rebuilds the | |
| 265 | 265 | // agent so tools / AGENTS.md / sandbox bind to the new cwd, and | |
| 266 | 266 | // opens a fresh session there. Returns an error if path doesn't | |
| @@ -690,7 +690,7 @@ type Interactive struct { | |||
| 690 | 690 | // shell escape, updated via BashTool progress for live rendering. | |
| 691 | 691 | shellLive string | |
| 692 | 692 | ||
| 693 | - | // awaitingStartupPre is true while the zotfile entry.pre auto-submit | |
| 693 | + | // awaitingStartupPre is true while the zutfile entry.pre auto-submit | |
| 694 | 694 | // is in flight. When it clears, deferredInitialInput is applied. | |
| 695 | 695 | awaitingStartupPre bool | |
| 696 | 696 | deferredInitialInput string | |
| @@ -870,7 +870,7 @@ func (i *Interactive) Run(ctx context.Context) error { | |||
| 870 | 870 | // selection over the wheel-speed boost, so we no longer turn it | |
| 871 | 871 | // on automatically. Wheel events fall through to the terminal's | |
| 872 | 872 | // own scrollback handler. | |
| 873 | - | // Keep zot on the terminal's main screen. We intentionally do not | |
| 873 | + | // Keep zut on the terminal's main screen. We intentionally do not | |
| 874 | 874 | // enter the alternate-screen buffer (CSI ?1049h). The renderer emits | |
| 875 | 875 | // chat as normal terminal flow/scrollback and redraws only the live | |
| 876 | 876 | // input/status block on normal typing. | |
| @@ -939,7 +939,7 @@ func (i *Interactive) Run(ctx context.Context) error { | |||
| 939 | 939 | // want to dismiss it (e.g. to check /help or /exit first). | |
| 940 | 940 | if i.agent == nil { | |
| 941 | 941 | i.statusErr = "not logged in. pick a login method below or press esc to dismiss." | |
| 942 | - | i.dialog.Open(i.cfg.ZotHome) | |
| 942 | + | i.dialog.Open(i.cfg.ZutHome) | |
| 943 | 943 | } | |
| 944 | 944 | ||
| 945 | 945 | // Input goroutine. Buffered generously so a drag-drop that the | |
| @@ -1441,7 +1441,7 @@ func (i *Interactive) buildChatLocked(cols int) []string { | |||
| 1441 | 1441 | } | |
| 1442 | 1442 | ||
| 1443 | 1443 | // Update-available banner: prepended above everything else so it's | |
| 1444 | - | // the first thing the user sees when opening a new zot session. | |
| 1444 | + | // the first thing the user sees when opening a new zut session. | |
| 1445 | 1445 | // Once rendered, it stays until the user updates to a newer | |
| 1446 | 1446 | // version — we don't persist a "dismissed" flag because this is | |
| 1447 | 1447 | // cheap and re-showing it is how most users remember to update. | |
| @@ -1515,7 +1515,7 @@ func (i *Interactive) sessionsRoot() string { | |||
| 1515 | 1515 | if i.cfg.SessionsRoot != "" { | |
| 1516 | 1516 | return i.cfg.SessionsRoot | |
| 1517 | 1517 | } | |
| 1518 | - | return i.cfg.ZotHome | |
| 1518 | + | return i.cfg.ZutHome | |
| 1519 | 1519 | } | |
| 1520 | 1520 | ||
| 1521 | 1521 | // lastCols returns the current terminal width in columns. | |
| @@ -1765,7 +1765,7 @@ func (i *Interactive) redraw() { | |||
| 1765 | 1765 | } | |
| 1766 | 1766 | ||
| 1767 | 1767 | // Busy prefix shown at the far left of the status bar. The | |
| 1768 | - | // spinner glyph and its funny-line message share the `zot` | |
| 1768 | + | // spinner glyph and its funny-line message share the `zut` | |
| 1769 | 1769 | // label colour (Theme.Assistant) so the whole "who's working" | |
| 1770 | 1770 | // band reads at a glance. Elapsed time stays muted because it | |
| 1771 | 1771 | // drifts every second and shouldn't grab focus. | |
| @@ -2149,7 +2149,7 @@ func hasImageEscape(line string) bool { | |||
| 2149 | 2149 | ||
| 2150 | 2150 | // snapViewportStartToImageBlock treats inline images as atomic blocks for | |
| 2151 | 2151 | // scrolling. Terminal image protocols draw from a single escape row into a | |
| 2152 | - | // separate graphics layer; the following blank rows are only zot's reserved | |
| 2152 | + | // separate graphics layer; the following blank rows are only zut's reserved | |
| 2153 | 2153 | // footprint. If the viewport starts on one of those blank rows, there is no | |
| 2154 | 2154 | // correct partial-image state to render. Snap back to the escape row instead | |
| 2155 | 2155 | // so the image is either shown from its beginning or skipped entirely. | |
| @@ -2857,7 +2857,7 @@ func (i *Interactive) handleKey(ctx context.Context, k tui.Key) (done bool) { | |||
| 2857 | 2857 | // ("be quiet" in a shell) rather than a deliberate | |
| 2858 | 2858 | // decision to kill a multi-minute model call that's | |
| 2859 | 2859 | // already cost tokens. Use esc to interrupt a turn; use | |
| 2860 | - | // a deliberate double-ctrl+c to exit zot entirely. First | |
| 2860 | + | // a deliberate double-ctrl+c to exit zut entirely. First | |
| 2861 | 2861 | // press arms the exit hint, second press within | |
| 2862 | 2862 | // ctrlCExitWindow quits. | |
| 2863 | 2863 | if i.busy { | |
| @@ -4094,7 +4094,7 @@ func (i *Interactive) openSettingsDialog() { | |||
| 4094 | 4094 | if themeName == "" { | |
| 4095 | 4095 | themeName = "auto" | |
| 4096 | 4096 | } | |
| 4097 | - | if themeName != "auto" && !tui.ThemeExists(i.cfg.ZotHome, themeName) { | |
| 4097 | + | if themeName != "auto" && !tui.ThemeExists(i.cfg.ZutHome, themeName) { | |
| 4098 | 4098 | themeName = "auto" | |
| 4099 | 4099 | i.cfg.ThemeName = "" | |
| 4100 | 4100 | if i.cfg.SettingsStore != nil { | |
| @@ -4104,7 +4104,7 @@ func (i *Interactive) openSettingsDialog() { | |||
| 4104 | 4104 | } | |
| 4105 | 4105 | themeOptions := []settingsOption{} | |
| 4106 | 4106 | themeChoice := 0 | |
| 4107 | - | availableThemes := tui.AvailableThemes(i.cfg.ZotHome) | |
| 4107 | + | availableThemes := tui.AvailableThemes(i.cfg.ZutHome) | |
| 4108 | 4108 | if i.cfg.ExtensionThemes != nil { | |
| 4109 | 4109 | availableThemes = append(availableThemes, i.cfg.ExtensionThemes()...) | |
| 4110 | 4110 | } | |
| @@ -4210,7 +4210,7 @@ func (i *Interactive) openSettingsDialog() { | |||
| 4210 | 4210 | { | |
| 4211 | 4211 | key: "auto_compact_threshold", | |
| 4212 | 4212 | label: "auto-compact threshold", | |
| 4213 | - | desc: "choose how full the model context can get before zot condenses conversation history", | |
| 4213 | + | desc: "choose how full the model context can get before zut condenses conversation history", | |
| 4214 | 4214 | options: autoCompactOptions, | |
| 4215 | 4215 | choice: autoCompactChoice, | |
| 4216 | 4216 | }, | |
| @@ -4809,7 +4809,7 @@ func (i *Interactive) applySettingToggle(key string, value bool) { | |||
| 4809 | 4809 | } | |
| 4810 | 4810 | } | |
| 4811 | 4811 | // Flip the live picker so the next @ reflects the new mode | |
| 4812 | - | // without restarting zot. SetRecursive drops its cache. | |
| 4812 | + | // without restarting zut. SetRecursive drops its cache. | |
| 4813 | 4813 | i.fileSuggest.SetRecursive(value) | |
| 4814 | 4814 | i.mu.Lock() | |
| 4815 | 4815 | i.statusOK = "recursive @-file search " + onOff(value) | |
| @@ -4982,13 +4982,13 @@ func (i *Interactive) applyThemeNow(name string) { | |||
| 4982 | 4982 | // themes live; inherited mode must not issue OSC queries while the TUI | |
| 4983 | 4983 | // already owns raw stdin. | |
| 4984 | 4984 | detected.Terminal = i.cfg.Theme.Terminal | |
| 4985 | - | th, applied, err := tui.LoadThemeFromHome(i.cfg.ZotHome, name, detected) | |
| 4985 | + | th, applied, err := tui.LoadThemeFromHome(i.cfg.ZutHome, name, detected) | |
| 4986 | 4986 | if err != nil { | |
| 4987 | 4987 | if i.cfg.SettingsStore != nil { | |
| 4988 | 4988 | _ = i.cfg.SettingsStore.SetTheme("auto") | |
| 4989 | 4989 | } | |
| 4990 | 4990 | i.cfg.ThemeName = "" | |
| 4991 | - | th, _, _ = tui.LoadThemeFromHome(i.cfg.ZotHome, "auto", detected) | |
| 4991 | + | th, _, _ = tui.LoadThemeFromHome(i.cfg.ZutHome, "auto", detected) | |
| 4992 | 4992 | i.mu.Lock() | |
| 4993 | 4993 | i.statusErr = "theme missing; reset to default" | |
| 4994 | 4994 | i.mu.Unlock() | |
| @@ -5334,7 +5334,7 @@ func (i *Interactive) runSlash(ctx context.Context, cmd string) (done bool) { | |||
| 5334 | 5334 | i.scrollOffset = 0 | |
| 5335 | 5335 | i.mu.Unlock() | |
| 5336 | 5336 | case "/login": | |
| 5337 | - | i.dialog.Open(i.cfg.ZotHome) | |
| 5337 | + | i.dialog.Open(i.cfg.ZutHome) | |
| 5338 | 5338 | case "/logout": | |
| 5339 | 5339 | if len(parts) >= 2 { | |
| 5340 | 5340 | // Explicit target: /logout anthropic | openai | all | |
| @@ -5431,7 +5431,7 @@ func (i *Interactive) runSlash(ctx context.Context, cmd string) (done bool) { | |||
| 5431 | 5431 | // Hidden command: switch the running session's cwd. Not in | |
| 5432 | 5432 | // slash_suggest, not in /help. Used by the workspaces | |
| 5433 | 5433 | // extension's panel-key Enter handler so picking a row | |
| 5434 | - | // jumps zot into that directory without relaunching. | |
| 5434 | + | // jumps zut into that directory without relaunching. | |
| 5435 | 5435 | // | |
| 5436 | 5436 | // Recovers the raw argument (path) from the original cmd | |
| 5437 | 5437 | // string rather than parts, so paths with spaces survive. | |
| @@ -5727,11 +5727,11 @@ func (i *Interactive) doLogout(target string) { | |||
| 5727 | 5727 | } | |
| 5728 | 5728 | ||
| 5729 | 5729 | func providerSetupInfo(provider string) (string, []string, bool) { | |
| 5730 | - | const docsURL = "https://raw.githubusercontent.com/patriceckhart/zot/main/docs/providers.md" | |
| 5730 | + | const docsURL = "https://raw.githubusercontent.com/bnema/zut/main/docs/providers.md" | |
| 5731 | 5731 | switch provider { | |
| 5732 | 5732 | case "amazon-bedrock": | |
| 5733 | 5733 | return "Amazon Bedrock setup", []string{ | |
| 5734 | - | "Amazon Bedrock uses AWS credentials instead of a generic zot API-key entry.", | |
| 5734 | + | "Amazon Bedrock uses AWS credentials instead of a generic zut API-key entry.", | |
| 5735 | 5735 | "Configure an AWS profile, IAM keys, bearer token, or role-based credentials.", | |
| 5736 | 5736 | "", | |
| 5737 | 5737 | "For Bedrock API keys, set:", | |
| @@ -6736,7 +6736,7 @@ func (i *Interactive) startTurnRequest(parent context.Context, prompt string, im | |||
| 6736 | 6736 | // "last frame had the previous turn's tool overlay" and | |
| 6737 | 6737 | // "this frame had it cleared above". Without this, the guard | |
| 6738 | 6738 | // reads delta = -(rows in cleared overlay) and decrements | |
| 6739 | - | // scrollOffset, which on terminals that mirror zot's pane | |
| 6739 | + | // scrollOffset, which on terminals that mirror zut's pane | |
| 6740 | 6740 | // scroll into the host scrollbar visibly yanks the viewport. | |
| 6741 | 6741 | // See autofollow_shrink_test.go for the exact arithmetic. | |
| 6742 | 6742 | i.prevChatLen = 0 | |
| @@ -6827,7 +6827,7 @@ func (i *Interactive) startTurnRequest(parent context.Context, prompt string, im | |||
| 6827 | 6827 | } | |
| 6828 | 6828 | // Persist the assistant's reply (and every tool row before | |
| 6829 | 6829 | // it) to the session file while the turn memory is hot. | |
| 6830 | - | // Without this, WriteNewTranscript only fires at zot exit, | |
| 6830 | + | // Without this, WriteNewTranscript only fires at zut exit, | |
| 6831 | 6831 | // meaning a crash or ungraceful kill drops the whole | |
| 6832 | 6832 | // conversation. FlushSession is idempotent (it advances the | |
| 6833 | 6833 | // baseline so subsequent flushes only write new rows). | |
| @@ -7145,7 +7145,7 @@ func (i *Interactive) handleEvent(ev core.AgentEvent) { | |||
| 7145 | 7145 | if tc, ok := i.toolCalls[e.ID]; ok { | |
| 7146 | 7146 | tc.RawJSONBuf += e.Delta | |
| 7147 | 7147 | // Refresh the live path as soon as it parses; used in | |
| 7148 | - | // the header (write /Users/pat/Desktop/demo.ts) | |
| 7148 | + | // the header (write /Users/example/Desktop/demo.ts) | |
| 7149 | 7149 | // while the content is still streaming. | |
| 7150 | 7150 | if p, pok, _ := tui.ExtractPartialStringField(tc.RawJSONBuf, "path"); pok { | |
| 7151 | 7151 | tc.LivePath = p | |
| @@ -7365,7 +7365,7 @@ func (i *Interactive) openTelegramDialog() { | |||
| 7365 | 7365 | items := i.telegramMenuItems() | |
| 7366 | 7366 | if len(items) == 0 { | |
| 7367 | 7367 | i.mu.Lock() | |
| 7368 | - | i.statusErr = "telegram not configured. run `zot telegram-bot setup` first." | |
| 7368 | + | i.statusErr = "telegram not configured. run `zut telegram-bot setup` first." | |
| 7369 | 7369 | i.mu.Unlock() | |
| 7370 | 7370 | i.invalidate() | |
| 7371 | 7371 | return | |
| @@ -7378,7 +7378,7 @@ func (i *Interactive) openTelegramDialog() { | |||
| 7378 | 7378 | // bridge state. Returns empty when no bot.json exists so the | |
| 7379 | 7379 | // caller can show a helpful status line instead of an empty menu. | |
| 7380 | 7380 | func (i *Interactive) telegramMenuItems() []telegramItem { | |
| 7381 | - | cfg, err := telegram.LoadConfig(i.cfg.ZotHome) | |
| 7381 | + | cfg, err := telegram.LoadConfig(i.cfg.ZutHome) | |
| 7382 | 7382 | if err != nil || cfg.BotToken == "" { | |
| 7383 | 7383 | return nil | |
| 7384 | 7384 | } | |
| @@ -7432,7 +7432,7 @@ func (i *Interactive) telegramConnect() { | |||
| 7432 | 7432 | i.invalidate() | |
| 7433 | 7433 | return | |
| 7434 | 7434 | } | |
| 7435 | - | cfg, err := telegram.LoadConfig(i.cfg.ZotHome) | |
| 7435 | + | cfg, err := telegram.LoadConfig(i.cfg.ZutHome) | |
| 7436 | 7436 | if err != nil { | |
| 7437 | 7437 | i.mu.Lock() | |
| 7438 | 7438 | i.statusErr = "telegram: " + err.Error() | |
| @@ -7442,7 +7442,7 @@ func (i *Interactive) telegramConnect() { | |||
| 7442 | 7442 | } | |
| 7443 | 7443 | if cfg.BotToken == "" { | |
| 7444 | 7444 | i.mu.Lock() | |
| 7445 | - | i.statusErr = "telegram: no bot token configured. run `zot telegram-bot setup` first." | |
| 7445 | + | i.statusErr = "telegram: no bot token configured. run `zut telegram-bot setup` first." | |
| 7446 | 7446 | i.mu.Unlock() | |
| 7447 | 7447 | i.invalidate() | |
| 7448 | 7448 | return | |
| @@ -7450,10 +7450,10 @@ func (i *Interactive) telegramConnect() { | |||
| 7450 | 7450 | // Refuse to start when a background daemon is already polling | |
| 7451 | 7451 | // the same bot. Two concurrent long-poll consumers race each | |
| 7452 | 7452 | // update and one always loses, so DMs get half-delivered. The | |
| 7453 | - | // user can `zot telegram-bot stop` first, then /telegram connect. | |
| 7454 | - | if pid, alive, _ := telegram.IsRunning(i.cfg.ZotHome); alive && pid > 0 { | |
| 7453 | + | // user can `zut telegram-bot stop` first, then /telegram connect. | |
| 7454 | + | if pid, alive, _ := telegram.IsRunning(i.cfg.ZutHome); alive && pid > 0 { | |
| 7455 | 7455 | i.mu.Lock() | |
| 7456 | - | i.statusErr = fmt.Sprintf("telegram: bot daemon already running (pid %d). stop it with `zot telegram-bot stop` first.", pid) | |
| 7456 | + | i.statusErr = fmt.Sprintf("telegram: bot daemon already running (pid %d). stop it with `zut telegram-bot stop` first.", pid) | |
| 7457 | 7457 | i.mu.Unlock() | |
| 7458 | 7458 | i.invalidate() | |
| 7459 | 7459 | return | |
| @@ -7462,7 +7462,7 @@ func (i *Interactive) telegramConnect() { | |||
| 7462 | 7462 | Client: telegram.NewClient(cfg.BotToken), | |
| 7463 | 7463 | Config: cfg, | |
| 7464 | 7464 | Save: func(next telegram.Config) error { | |
| 7465 | - | return telegram.SaveConfig(i.cfg.ZotHome, next) | |
| 7465 | + | return telegram.SaveConfig(i.cfg.ZutHome, next) | |
| 7466 | 7466 | }, | |
| 7467 | 7467 | Host: &telegramHost{iv: i}, | |
| 7468 | 7468 | } | |
| @@ -7884,12 +7884,12 @@ func (i *Interactive) telegramStatus() { | |||
| 7884 | 7884 | } else { | |
| 7885 | 7885 | msg += " - awaiting pairing" | |
| 7886 | 7886 | } | |
| 7887 | - | } else if pid, alive, _ := telegram.IsRunning(i.cfg.ZotHome); alive && pid > 0 { | |
| 7887 | + | } else if pid, alive, _ := telegram.IsRunning(i.cfg.ZutHome); alive && pid > 0 { | |
| 7888 | 7888 | msg = fmt.Sprintf("telegram: background daemon running (pid %d) - /telegram connect won't work until you stop it", pid) | |
| 7889 | 7889 | } else { | |
| 7890 | - | cfg, _ := telegram.LoadConfig(i.cfg.ZotHome) | |
| 7890 | + | cfg, _ := telegram.LoadConfig(i.cfg.ZutHome) | |
| 7891 | 7891 | if cfg.BotToken == "" { | |
| 7892 | - | msg = "telegram: not configured. run `zot telegram-bot setup` first." | |
| 7892 | + | msg = "telegram: not configured. run `zut telegram-bot setup` first." | |
| 7893 | 7893 | } else { | |
| 7894 | 7894 | msg = "telegram: disconnected" | |
| 7895 | 7895 | if cfg.BotUsername != "" { | |
| @@ -7963,8 +7963,8 @@ func (h *telegramHost) Notify(level, message string) { | |||
| 7963 | 7963 | // transcript on fork; no parent/siblings on tree). | |
| 7964 | 7964 | func (i *Interactive) openSessionOpsDialog() { | |
| 7965 | 7965 | items := []sessionOpsItem{ | |
| 7966 | - | {label: "export", action: "export", hint: "write the current session to a .zotsession file"}, | |
| 7967 | - | {label: "import", action: "import", hint: "load a .zotsession file into this directory"}, | |
| 7966 | + | {label: "export", action: "export", hint: "write the current session to a .zutsession file"}, | |
| 7967 | + | {label: "import", action: "import", hint: "load a .zutsession file into this directory"}, | |
| 7968 | 7968 | {label: "fork", action: "fork", hint: "branch from a past user message into a new session"}, | |
| 7969 | 7969 | {label: "tree", action: "tree", hint: "switch between branches in this directory"}, | |
| 7970 | 7970 | } | |
| @@ -8034,7 +8034,7 @@ func (i *Interactive) doSessionExport(dst string) { | |||
| 8034 | 8034 | i.invalidate() | |
| 8035 | 8035 | } | |
| 8036 | 8036 | ||
| 8037 | - | // doSessionImport copies the .zotsession file at src into the | |
| 8037 | + | // doSessionImport copies the .zutsession file at src into the | |
| 8038 | 8038 | // running cwd's sessions directory and loads it as the active | |
| 8039 | 8039 | // session, same as `/sessions` -> pick. When src is empty we ask | |
| 8040 | 8040 | // the user to pass a path (no usable default here). | |
| @@ -8042,7 +8042,7 @@ func (i *Interactive) doSessionImport(src string) { | |||
| 8042 | 8042 | src = unquotePath(src) | |
| 8043 | 8043 | if src == "" { | |
| 8044 | 8044 | i.mu.Lock() | |
| 8045 | - | i.statusErr = "import: pass a path — e.g. /session import ~/Downloads/work.zotsession" | |
| 8045 | + | i.statusErr = "import: pass a path — e.g. /session import ~/Downloads/work.zutsession" | |
| 8046 | 8046 | i.mu.Unlock() | |
| 8047 | 8047 | i.invalidate() | |
| 8048 | 8048 | return | |
M packages/agent/modes/interactive_model_test.go +1 -1
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "testing" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/tui" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | func TestReasoningSelectorUsesCurrentModelLevels(t *testing.T) { | |
M packages/agent/modes/jail_setting_test.go +1 -1
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 6 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestJailByDefaultSettingUpdatesLiveSandbox(t *testing.T) { | |
M packages/agent/modes/json.go +3 -3
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "fmt" | |
| 7 | 7 | "io" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // RunJSON runs the agent to completion, writing one JSON object per | |
| @@ -35,7 +35,7 @@ func RunJSON(ctx context.Context, ag *core.Agent, prompt string, images []provid | |||
| 35 | 35 | ||
| 36 | 36 | // EventToJSON converts an AgentEvent to a JSON-friendly map. The on-wire | |
| 37 | 37 | // schema is deliberately simple and flat. Exported so the RPC mode can | |
| 38 | - | // reuse the same serialisation as `zot --json`. | |
| 38 | + | // reuse the same serialisation as `zut --json`. | |
| 39 | 39 | func EventToJSON(ev core.AgentEvent) map[string]any { | |
| 40 | 40 | m := map[string]any{"type": ev.Type()} | |
| 41 | 41 | switch e := ev.(type) { | |
M packages/agent/modes/jump_dialog.go +2 -2
| @@ -4,8 +4,8 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | "strings" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/provider" | |
| 8 | + | "github.com/bnema/zut/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // jumpTarget describes one "turn" in the current session — a user | |
M packages/agent/modes/llama_dialog.go +2 -2
| @@ -8,9 +8,9 @@ import ( | |||
| 8 | 8 | "sync" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 13 | "github.com/mattn/go-runewidth" | |
| 12 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | type llamaDialogStep int | |
M packages/agent/modes/llama_dialog_test.go +2 -2
| @@ -8,8 +8,8 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 12 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | + | "github.com/bnema/zut/packages/tui" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | func TestLlamaDialogSearchesAsUserTypes(t *testing.T) { | |
M packages/agent/modes/login_dialog.go +6 -6
| @@ -6,9 +6,9 @@ import ( | |||
| 6 | 6 | "sort" | |
| 7 | 7 | "strings" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider/auth" | |
| 11 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/provider/auth" | |
| 11 | + | "github.com/bnema/zut/packages/tui" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // loginStep is the current node in the login dialog state machine. | |
| @@ -66,11 +66,11 @@ func (d *loginDialog) Active() bool { return d != nil && d.step != loginStepClos | |||
| 66 | 66 | ||
| 67 | 67 | // Open starts the dialog from scratch and captures the current | |
| 68 | 68 | // login status for each provider so the picker can show it. | |
| 69 | - | // zotHome is the zot state directory ($ZOT_HOME); auth.json | |
| 69 | + | // zutHome is the zut state directory ($ZUT_HOME); auth.json | |
| 70 | 70 | // lives inside it. Passing the path in (instead of importing | |
| 71 | 71 | // the agent package to call AuthPath()) avoids a cyclic import | |
| 72 | 72 | // between agent and agent/modes. | |
| 73 | - | func (d *loginDialog) Open(zotHome string) { | |
| 73 | + | func (d *loginDialog) Open(zutHome string) { | |
| 74 | 74 | d.step = loginStepMethod | |
| 75 | 75 | d.method = "" | |
| 76 | 76 | d.provider = "" | |
| @@ -94,7 +94,7 @@ func (d *loginDialog) Open(zotHome string) { | |||
| 94 | 94 | // provider as not-logged-in. The status line just won't show | |
| 95 | 95 | // anything useful in that case, which is fine — the user | |
| 96 | 96 | // was about to log in anyway. | |
| 97 | - | path := filepath.Join(zotHome, "auth.json") | |
| 97 | + | path := filepath.Join(zutHome, "auth.json") | |
| 98 | 98 | if creds, err := auth.NewStore(path).Load(); err == nil { | |
| 99 | 99 | d.status["anthropic"] = creds.Method("anthropic") | |
| 100 | 100 | d.status["openai"] = "" | |
M packages/agent/modes/login_dialog_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestLoginDialogProviderPickerFiltersLikeModelPicker(t *testing.T) { | |
M packages/agent/modes/logout_dialog.go +1 -1
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | package modes | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 4 | + | "github.com/bnema/zut/packages/tui" | |
| 5 | 5 | ) | |
| 6 | 6 | ||
| 7 | 7 | // logoutDialog is the picker shown when the user runs `/logout` | |
M packages/agent/modes/logout_dialog_test.go +1 -1
| @@ -4,8 +4,8 @@ import ( | |||
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 7 | 8 | "github.com/mattn/go-runewidth" | |
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | func TestLogoutDialogSelectionBackgroundSpansFullWidth(t *testing.T) { | |
M packages/agent/modes/model_dialog.go +2 -2
| @@ -5,9 +5,9 @@ import ( | |||
| 5 | 5 | "sort" | |
| 6 | 6 | "strings" | |
| 7 | 7 | ||
| 8 | + | "github.com/bnema/zut/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/tui" | |
| 8 | 10 | "github.com/mattn/go-runewidth" | |
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // modelDialog is an inline picker for choosing the active model. | |
M packages/agent/modes/ponytail_setting_test.go +1 -1
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/core" | |
| 8 | + | "github.com/bnema/zut/packages/core" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | type recordingPonytailSettingsStore struct { | |
M packages/agent/modes/print.go +3 -3
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // Package modes implements zot's three run modes: print, json, interactive. | |
| 1 | + | // Package modes implements zut's three run modes: print, json, interactive. | |
| 2 | 2 | package modes | |
| 3 | 3 | ||
| 4 | 4 | import ( | |
| @@ -7,8 +7,8 @@ import ( | |||
| 7 | 7 | "io" | |
| 8 | 8 | "strings" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // RunPrint runs the agent to completion and writes only the final | |
M packages/agent/modes/print_test.go +2 -2
| @@ -5,8 +5,8 @@ import ( | |||
| 5 | 5 | "context" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/core" | |
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 8 | + | "github.com/bnema/zut/packages/core" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | type printTestClient struct{} | |
M packages/agent/modes/reload_ext.go +2 -2
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | const reloadStatusDuration = 5 * time.Second | |
M packages/agent/modes/reload_ext_test.go +3 -3
| @@ -5,9 +5,9 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 8 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func TestFormatReloadStatusIncludesErrorDetails(t *testing.T) { | |
M packages/agent/modes/rescue_dialog.go +2 -2
| @@ -4,8 +4,8 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | "strings" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/provider" | |
| 8 | + | "github.com/bnema/zut/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // rescueDialog offers a quick model swap when the active turn fails | |
M packages/agent/modes/session_agent_root_test.go +8 -8
| @@ -4,12 +4,12 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/core" | |
| 8 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 7 | + | "github.com/bnema/zut/packages/core" | |
| 8 | + | "github.com/bnema/zut/packages/provider" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | func TestSessionsSlashUsesAgentSessionRoot(t *testing.T) { | |
| 12 | - | zotHome := t.TempDir() | |
| 12 | + | zutHome := t.TempDir() | |
| 13 | 13 | agentRoot := t.TempDir() | |
| 14 | 14 | cwd := t.TempDir() | |
| 15 | 15 | ||
| @@ -28,7 +28,7 @@ func TestSessionsSlashUsesAgentSessionRoot(t *testing.T) { | |||
| 28 | 28 | } | |
| 29 | 29 | ||
| 30 | 30 | i := NewInteractive(InteractiveConfig{ | |
| 31 | - | ZotHome: zotHome, | |
| 31 | + | ZutHome: zutHome, | |
| 32 | 32 | SessionsRoot: agentRoot, | |
| 33 | 33 | CWD: cwd, | |
| 34 | 34 | }) | |
| @@ -46,9 +46,9 @@ func TestSessionsSlashUsesAgentSessionRoot(t *testing.T) { | |||
| 46 | 46 | } | |
| 47 | 47 | } | |
| 48 | 48 | ||
| 49 | - | func TestSessionsRootDefaultsToZotHome(t *testing.T) { | |
| 50 | - | i := &Interactive{cfg: InteractiveConfig{ZotHome: "/zot/home"}} | |
| 51 | - | if got := i.sessionsRoot(); got != "/zot/home" { | |
| 52 | - | t.Fatalf("sessions root = %q, want ZotHome fallback", got) | |
| 49 | + | func TestSessionsRootDefaultsToZutHome(t *testing.T) { | |
| 50 | + | i := &Interactive{cfg: InteractiveConfig{ZutHome: "/zut/home"}} | |
| 51 | + | if got := i.sessionsRoot(); got != "/zut/home" { | |
| 52 | + | t.Fatalf("sessions root = %q, want ZutHome fallback", got) | |
| 53 | 53 | } | |
| 54 | 54 | } | |
M packages/agent/modes/session_dialog.go +2 -2
| @@ -7,8 +7,8 @@ import ( | |||
| 7 | 7 | "sync" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 11 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | + | "github.com/bnema/zut/packages/tui" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | type sessionLoadEventKind uint8 | |
M packages/agent/modes/session_dialog_test.go +3 -3
| @@ -8,9 +8,9 @@ import ( | |||
| 8 | 8 | "time" | |
| 9 | 9 | "unicode" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/core" | |
| 12 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 11 | + | "github.com/bnema/zut/packages/core" | |
| 12 | + | "github.com/bnema/zut/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/tui" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | func TestFormatSessionRowPlainSanitizesControlBytes(t *testing.T) { | |
M packages/agent/modes/session_ops_dialog.go +2 -2
| @@ -1,13 +1,13 @@ | |||
| 1 | 1 | package modes | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 4 | + | "github.com/bnema/zut/packages/tui" | |
| 5 | 5 | ) | |
| 6 | 6 | ||
| 7 | 7 | // sessionOpsDialog is the picker shown when the user runs `/session` | |
| 8 | 8 | // without an argument. Offers the two portable-file operations on | |
| 9 | 9 | // the current conversation: export (write the in-memory transcript | |
| 10 | - | // plus meta to a .zotsession file) and import (load a .zotsession | |
| 10 | + | // plus meta to a .zutsession file) and import (load a .zutsession | |
| 11 | 11 | // from another machine and swap it in as the active session). | |
| 12 | 12 | // | |
| 13 | 13 | // Shape mirrors telegramDialog and logoutDialog: tiny list, arrow | |
M packages/agent/modes/session_title.go +4 -4
| @@ -5,9 +5,9 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | "time" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/core" | |
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 8 | + | "github.com/bnema/zut/packages/core" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | const sessionTitleGenerationTimeout = 10 * time.Second | |
| @@ -126,7 +126,7 @@ func (i *Interactive) writeTerminalTitleLocked(subject string) { | |||
| 126 | 126 | if subject != "" && !terminalTitleEnabled(i.cfg.TerminalTitleEnabled) { | |
| 127 | 127 | return | |
| 128 | 128 | } | |
| 129 | - | display := "zot" | |
| 129 | + | display := "zut" | |
| 130 | 130 | if subject != "" { | |
| 131 | 131 | display += ": " + subject | |
| 132 | 132 | } | |
M packages/agent/modes/session_title_test.go +5 -5
| @@ -8,8 +8,8 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/core" | |
| 12 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/core" | |
| 12 | + | "github.com/bnema/zut/packages/provider" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | type interactiveTitleClient struct { | |
| @@ -119,7 +119,7 @@ func TestFirstRealPromptSetsAndPersistsTerminalTitle(t *testing.T) { | |||
| 119 | 119 | case <-time.After(time.Second): | |
| 120 | 120 | t.Fatal("title was not persisted") | |
| 121 | 121 | } | |
| 122 | - | waitTerminalOutput(t, term, "\x1b]0;zot: Fix login retries\x07") | |
| 122 | + | waitTerminalOutput(t, term, "\x1b]0;zut: Fix login retries\x07") | |
| 123 | 123 | if got := len(ag.Messages()); got != 2 { | |
| 124 | 124 | t.Fatalf("transcript message count = %d, want user + assistant only", got) | |
| 125 | 125 | } | |
| @@ -264,7 +264,7 @@ func TestDisablingTerminalTitlesRestoresNeutralTitle(t *testing.T) { | |||
| 264 | 264 | i.applySettingToggle("terminal_title_enabled", false) | |
| 265 | 265 | ||
| 266 | 266 | got := term.String() | |
| 267 | - | if !strings.Contains(got, "\x1b]0;zot: Existing work\x07") || !strings.Contains(got, "\x1b]0;zot\x07") { | |
| 267 | + | if !strings.Contains(got, "\x1b]0;zut: Existing work\x07") || !strings.Contains(got, "\x1b]0;zut\x07") { | |
| 268 | 268 | t.Fatalf("terminal title output = %q, want existing and neutral title sequences", got) | |
| 269 | 269 | } | |
| 270 | 270 | } | |
| @@ -283,7 +283,7 @@ func TestLoadedSessionTitleIsRestoredWithoutGeneration(t *testing.T) { | |||
| 283 | 283 | }) | |
| 284 | 284 | i.markInteractiveStarted() | |
| 285 | 285 | ||
| 286 | - | if got, want := term.String(), "\x1b]0;zot: Existing work\x07"; got != want { | |
| 286 | + | if got, want := term.String(), "\x1b]0;zut: Existing work\x07"; got != want { | |
| 287 | 287 | t.Fatalf("restored terminal title = %q, want %q", got, want) | |
| 288 | 288 | } | |
| 289 | 289 | i.mu.Lock() | |
M packages/agent/modes/session_tree_dialog.go +5 -5
| @@ -8,9 +8,9 @@ import ( | |||
| 8 | 8 | ||
| 9 | 9 | "github.com/mattn/go-runewidth" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/core" | |
| 12 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 11 | + | "github.com/bnema/zut/packages/core" | |
| 12 | + | "github.com/bnema/zut/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/tui" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | // sessionTreeBoundary describes what a tree row points at. Message rows are | |
| @@ -738,7 +738,7 @@ func sessionTreeRoleLabel(role provider.Role) string { | |||
| 738 | 738 | case provider.RoleUser: | |
| 739 | 739 | return "you" | |
| 740 | 740 | case provider.RoleAssistant: | |
| 741 | - | return "zot" | |
| 741 | + | return "zut" | |
| 742 | 742 | case provider.RoleTool: | |
| 743 | 743 | return "tool" | |
| 744 | 744 | default: | |
| @@ -822,7 +822,7 @@ func colorSessionTreeLine(th tui.Theme, line string) string { | |||
| 822 | 822 | switch { | |
| 823 | 823 | case strings.HasPrefix(plain, "you:"): | |
| 824 | 824 | return th.FGColor(th.FG, line) | |
| 825 | - | case strings.HasPrefix(plain, "zot:"): | |
| 825 | + | case strings.HasPrefix(plain, "zut:"): | |
| 826 | 826 | return th.FGColor(th.Muted, line) | |
| 827 | 827 | case strings.HasPrefix(plain, "tool:"): | |
| 828 | 828 | return th.FGColor(th.ToolOut, line) | |
M packages/agent/modes/session_tree_dialog_test.go +3 -3
| @@ -6,9 +6,9 @@ import ( | |||
| 6 | 6 | "testing" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/tui" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | func TestSessionTreeTargetCarriesEffectiveBoundaryAndDraft(t *testing.T) { | |
M packages/agent/modes/settings_dialog.go +1 -1
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | ||
| 6 | 6 | "github.com/mattn/go-runewidth" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 8 | + | "github.com/bnema/zut/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | type settingsDialog struct { | |
M packages/agent/modes/shell_escape_test.go +4 -4
| @@ -7,15 +7,15 @@ import ( | |||
| 7 | 7 | "testing" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func TestShellEscapeAddsOutputToAgentContext(t *testing.T) { | |
| 14 | 14 | agent := core.NewAgent(nil, "", "", nil) | |
| 15 | 15 | i := NewInteractive(InteractiveConfig{Agent: agent, CWD: t.TempDir()}) | |
| 16 | - | cmd := "printf zot-shell-context" | |
| 16 | + | cmd := "printf zut-shell-context" | |
| 17 | 17 | if runtime.GOOS == "windows" { | |
| 18 | - | cmd = "echo zot-shell-context" | |
| 18 | + | cmd = "echo zut-shell-context" | |
| 19 | 19 | } | |
| 20 | 20 | ||
| 21 | 21 | i.startShellEscape(context.Background(), cmd) | |
| @@ -28,7 +28,7 @@ func TestShellEscapeAddsOutputToAgentContext(t *testing.T) { | |||
| 28 | 28 | i.mu.Unlock() | |
| 29 | 29 | if len(messages) == 1 && !running { | |
| 30 | 30 | text := userMessageText(messages[0]) | |
| 31 | - | if !strings.Contains(text, "zot-shell-context") { | |
| 31 | + | if !strings.Contains(text, "zut-shell-context") { | |
| 32 | 32 | t.Fatalf("shell context = %q, want command output", text) | |
| 33 | 33 | } | |
| 34 | 34 | if messages[0].Meta[shellEscapeMetaKey] != "true" { | |
M packages/agent/modes/skill_command.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | "strings" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 7 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | const skillCommandPrefix = "/skill:" | |
M packages/agent/modes/skill_command_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 7 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestExpandSkillCommandForcesNamedSkill(t *testing.T) { | |
M packages/agent/modes/skills_dialog.go +3 -3
| @@ -4,8 +4,8 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | "strings" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 8 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 8 | + | "github.com/bnema/zut/packages/tui" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // skillsDialog lists every discovered skill and lets the user view | |
| @@ -100,7 +100,7 @@ func (d *skillsDialog) Render(th tui.Theme, width int) []string { | |||
| 100 | 100 | out := []string{frameHeader(th, "skills (enter to view, esc to close)", width)} | |
| 101 | 101 | if len(d.skills) == 0 { | |
| 102 | 102 | out = append(out, " "+th.FGColor(th.Muted, "no user skills loaded")) | |
| 103 | - | out = append(out, " "+th.FGColor(th.Muted, "add SKILL.md under $ZOT_HOME/skills, .zot/skills, .claude/skills, or .agents/skills")) | |
| 103 | + | out = append(out, " "+th.FGColor(th.Muted, "add SKILL.md under $ZUT_HOME/skills, .zut/skills, .claude/skills, or .agents/skills")) | |
| 104 | 104 | out = append(out, frameRule(th, width)) | |
| 105 | 105 | return out | |
| 106 | 106 | } | |
M packages/agent/modes/slash_case_test.go +2 -2
| @@ -21,8 +21,8 @@ func TestRunSlashPreservesArgumentCase(t *testing.T) { | |||
| 21 | 21 | return errors.New("stop after capture") | |
| 22 | 22 | }, | |
| 23 | 23 | }} | |
| 24 | - | i.runSlash(context.Background(), "/CD /Users/Pat/Mixed Case") | |
| 25 | - | if got != "/Users/Pat/Mixed Case" { | |
| 24 | + | i.runSlash(context.Background(), "/CD /Users/Example/Mixed Case") | |
| 25 | + | if got != "/Users/Example/Mixed Case" { | |
| 26 | 26 | t.Fatalf("ChangeCWD path = %q, want original argument case", got) | |
| 27 | 27 | } | |
| 28 | 28 | } | |
M packages/agent/modes/slash_suggest.go +4 -4
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | ||
| 7 | 7 | "github.com/mattn/go-runewidth" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // slashCommand is one entry in the autocomplete popup. Header rows | |
| @@ -67,7 +67,7 @@ var slashCatalog = []slashCommand{ | |||
| 67 | 67 | {Name: "/telegram", Desc: "connect, disconnect, or show status of the telegram bridge"}, | |
| 68 | 68 | {Name: "/settings", Desc: "open settings"}, | |
| 69 | 69 | {Name: "/clear", Desc: "clear the chat transcript"}, | |
| 70 | - | {Name: "/exit", Desc: "exit zot"}, | |
| 70 | + | {Name: "/exit", Desc: "exit zut"}, | |
| 71 | 71 | } | |
| 72 | 72 | ||
| 73 | 73 | // slashSuggester renders the popup that appears when the editor starts | |
| @@ -205,7 +205,7 @@ func (s *slashSuggester) baseCatalog() []slashCommand { | |||
| 205 | 205 | ||
| 206 | 206 | // looksLikeSlashCommand reports whether text is an attempt at a slash | |
| 207 | 207 | // command (valid or not). Returns true for things like "/foo" or | |
| 208 | - | // "/bar baz" but false for paths ("/Users/pat/...") and regexes | |
| 208 | + | // "/bar baz" but false for paths ("/Users/example/...") and regexes | |
| 209 | 209 | // ("/foo.bar/") so those can be sent to the model as-is. | |
| 210 | 210 | // | |
| 211 | 211 | // The head after "/" must be a single simple word: only letters, | |
M packages/agent/modes/slash_suggest_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "slices" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/agent/skills" | |
| 7 | + | "github.com/bnema/zut/packages/agent/skills" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestSlashSuggesterHidesUnjailUntilJailed(t *testing.T) { | |
M packages/agent/modes/spinner.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "math/rand" | |
| 5 | 5 | "time" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | // spinner drives the busy animation shown in the status bar while a | |
M packages/agent/modes/startup_instructions_test.go +2 -2
| @@ -4,7 +4,7 @@ import "testing" | |||
| 4 | 4 | ||
| 5 | 5 | func TestStartupResourcesAreOptIn(t *testing.T) { | |
| 6 | 6 | cfg := InteractiveConfig{ | |
| 7 | - | StartupAgentName: "zot-maintenance", | |
| 7 | + | StartupAgentName: "zut-maintenance", | |
| 8 | 8 | StartupContextPaths: []string{"/repo/AGENTS.md"}, | |
| 9 | 9 | StartupExtensionNames: []string{"workspaces"}, | |
| 10 | 10 | StartupSkillNames: []string{"review"}, | |
| @@ -30,7 +30,7 @@ func assertStartupResourceCounts(t *testing.T, i *Interactive, want int) { | |||
| 30 | 30 | t.Helper() | |
| 31 | 31 | wantAgent := "" | |
| 32 | 32 | if want > 0 { | |
| 33 | - | wantAgent = "zot-maintenance" | |
| 33 | + | wantAgent = "zut-maintenance" | |
| 34 | 34 | } | |
| 35 | 35 | if got := i.view.StartupAgentName; got != wantAgent { | |
| 36 | 36 | t.Fatalf("startup agent = %q, want %q", got, wantAgent) | |
M packages/agent/modes/startup_pre_test.go +8 -8
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "testing" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func applyStartupPreResultForTest(t *testing.T, i *Interactive) { | |
| @@ -66,9 +66,9 @@ func TestStartupPreShellWithoutAgentThenPrefill(t *testing.T) { | |||
| 66 | 66 | i.deferredInitialInput = "after-pre" | |
| 67 | 67 | i.autoSubmitDeferred = false | |
| 68 | 68 | ||
| 69 | - | cmd := "printf zot-pre" | |
| 69 | + | cmd := "printf zut-pre" | |
| 70 | 70 | if runtime.GOOS == "windows" { | |
| 71 | - | cmd = "echo zot-pre" | |
| 71 | + | cmd = "echo zut-pre" | |
| 72 | 72 | } | |
| 73 | 73 | i.startShellEscape(context.Background(), cmd) | |
| 74 | 74 | ||
| @@ -99,15 +99,15 @@ func TestStartupPreShellThenAutoSubmitShell(t *testing.T) { | |||
| 99 | 99 | i := NewInteractive(InteractiveConfig{Agent: agent, CWD: t.TempDir()}) | |
| 100 | 100 | i.runCtx = context.Background() | |
| 101 | 101 | i.awaitingStartupPre = true | |
| 102 | - | i.deferredInitialInput = "!printf zot-default" | |
| 102 | + | i.deferredInitialInput = "!printf zut-default" | |
| 103 | 103 | if runtime.GOOS == "windows" { | |
| 104 | - | i.deferredInitialInput = "!echo zot-default" | |
| 104 | + | i.deferredInitialInput = "!echo zut-default" | |
| 105 | 105 | } | |
| 106 | 106 | i.autoSubmitDeferred = true | |
| 107 | 107 | ||
| 108 | - | cmd := "printf zot-pre" | |
| 108 | + | cmd := "printf zut-pre" | |
| 109 | 109 | if runtime.GOOS == "windows" { | |
| 110 | - | cmd = "echo zot-pre" | |
| 110 | + | cmd = "echo zut-pre" | |
| 111 | 111 | } | |
| 112 | 112 | i.startShellEscape(context.Background(), cmd) | |
| 113 | 113 | ||
| @@ -125,7 +125,7 @@ func TestStartupPreShellThenAutoSubmitShell(t *testing.T) { | |||
| 125 | 125 | i.mu.Unlock() | |
| 126 | 126 | if len(messages) >= 2 && !running && !awaiting { | |
| 127 | 127 | joined := userMessageText(messages[0]) + "\n" + userMessageText(messages[1]) | |
| 128 | - | if !strings.Contains(joined, "zot-pre") || !strings.Contains(joined, "zot-default") { | |
| 128 | + | if !strings.Contains(joined, "zut-pre") || !strings.Contains(joined, "zut-default") { | |
| 129 | 129 | t.Fatalf("context = %q, want both pre and default shell output", joined) | |
| 130 | 130 | } | |
| 131 | 131 | return | |
M packages/agent/modes/stream.go +2 -2
| @@ -7,8 +7,8 @@ import ( | |||
| 7 | 7 | "os" | |
| 8 | 8 | "strings" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // RunStream runs the agent and writes assistant text to out as it | |
M packages/agent/modes/stream_test.go +2 -2
| @@ -5,8 +5,8 @@ import ( | |||
| 5 | 5 | "context" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/core" | |
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 8 | + | "github.com/bnema/zut/packages/core" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | type streamTestClient struct { | |
M packages/agent/modes/subagents_dialog.go +4 -4
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 9 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // subagentsDialog is the dashboard shown by /subagents (no argument) and | |
| @@ -132,10 +132,10 @@ func (d *subagentsDialog) editorPrompt(th tui.Theme) string { | |||
| 132 | 132 | ||
| 133 | 133 | // SetCurrentModel pins the model + provider every fresh spawn will | |
| 134 | 134 | // inherit. The host wires this to the same Model / Provider the rest | |
| 135 | - | // of zot is currently using, so agents started from the dashboard | |
| 135 | + | // of zut is currently using, so agents started from the dashboard | |
| 136 | 136 | // run on whatever the user last picked via /model. Pass empty | |
| 137 | 137 | // strings to clear the override (the child then resolves its own | |
| 138 | - | // default the same way a bare `zot` invocation does). | |
| 138 | + | // default the same way a bare `zut` invocation does). | |
| 139 | 139 | func (d *subagentsDialog) SetCurrentModel(model, providerID string) { | |
| 140 | 140 | d.pendingModel = model | |
| 141 | 141 | d.pendingProvider = providerID | |
M packages/agent/modes/subagents_dialog_test.go +3 -3
| @@ -9,9 +9,9 @@ import ( | |||
| 9 | 9 | "testing" | |
| 10 | 10 | "time" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 14 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 12 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | + | "github.com/bnema/zut/packages/tui" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | // staticSnapshots returns a deterministic snapshot slice for the | |
M packages/agent/modes/subagents_slash.go +3 -3
| @@ -5,9 +5,9 @@ import ( | |||
| 5 | 5 | "fmt" | |
| 6 | 6 | "strings" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 8 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/tui" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // runSubagents dispatches /subagents subcommands. Layout: | |
M packages/agent/modes/subagents_slash_test.go +1 -1
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "testing" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 10 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // newInteractiveForSupervisorTest builds the minimal Interactive scaffolding | |
M packages/agent/modes/telegram/adapter.go +4 -4
| @@ -7,8 +7,8 @@ import ( | |||
| 7 | 7 | "strings" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/agent/modes/bot" | |
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/agent/modes/bot" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // Adapter implements bot.BotAdapter for Telegram. | |
| @@ -31,7 +31,7 @@ func (a *Adapter) Run(ctx context.Context, | |||
| 31 | 31 | commandHandler func(bot.Command, bot.InboundMessage), | |
| 32 | 32 | ) error { | |
| 33 | 33 | if a.Cfg.BotToken == "" { | |
| 34 | - | return fmt.Errorf("no bot token configured; run `zot bot setup` first") | |
| 34 | + | return fmt.Errorf("no bot token configured; run `zut bot setup` first") | |
| 35 | 35 | } | |
| 36 | 36 | me, err := a.Client.GetMe(ctx) | |
| 37 | 37 | if err != nil { | |
| @@ -121,7 +121,7 @@ func (a *Adapter) handleUpdate(ctx context.Context, u Update, | |||
| 121 | 121 | a.Cfg.AllowedUserID = msg.From.ID | |
| 122 | 122 | _ = a.Save(*a.Cfg) | |
| 123 | 123 | _ = a.Client.SendMessage(ctx, msg.Chat.ID, | |
| 124 | - | fmt.Sprintf("paired with @%s. send any message and i'll forward it to zot.", msg.From.Username), | |
| 124 | + | fmt.Sprintf("paired with @%s. send any message and i'll forward it to zut.", msg.From.Username), | |
| 125 | 125 | msg.MessageID) | |
| 126 | 126 | return | |
| 127 | 127 | } | |
M packages/agent/modes/telegram/api_test.go +7 -7
| @@ -12,8 +12,8 @@ import ( | |||
| 12 | 12 | "testing" | |
| 13 | 13 | "time" | |
| 14 | 14 | ||
| 15 | - | "github.com/patriceckhart/zot/packages/agent/modes/bot" | |
| 16 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 15 | + | "github.com/bnema/zut/packages/agent/modes/bot" | |
| 16 | + | "github.com/bnema/zut/packages/provider" | |
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | 19 | type roundTripFunc func(*http.Request) (*http.Response, error) | |
| @@ -111,7 +111,7 @@ func TestAdapterRunDeletesWebhookBeforePolling(t *testing.T) { | |||
| 111 | 111 | mu.Unlock() | |
| 112 | 112 | switch endpoint { | |
| 113 | 113 | case "getMe": | |
| 114 | - | return telegramResponse(`{"ok":true,"result":{"id":1,"is_bot":true,"username":"zot_test"}}`), nil | |
| 114 | + | return telegramResponse(`{"ok":true,"result":{"id":1,"is_bot":true,"username":"zut_test"}}`), nil | |
| 115 | 115 | case "deleteWebhook": | |
| 116 | 116 | return telegramResponse(`{"ok":true,"result":true}`), nil | |
| 117 | 117 | case "getUpdates": | |
| @@ -122,7 +122,7 @@ func TestAdapterRunDeletesWebhookBeforePolling(t *testing.T) { | |||
| 122 | 122 | return nil, errors.New("unreachable") | |
| 123 | 123 | } | |
| 124 | 124 | })) | |
| 125 | - | cfg := Config{BotToken: "test", BotID: 1, BotUsername: "zot_test"} | |
| 125 | + | cfg := Config{BotToken: "test", BotID: 1, BotUsername: "zut_test"} | |
| 126 | 126 | adapter := NewAdapter(client, &cfg, func(Config) error { return nil }) | |
| 127 | 127 | ||
| 128 | 128 | err := adapter.Run(ctx, func(bot.InboundMessage) {}, func(bot.Command, bot.InboundMessage) {}) | |
| @@ -151,7 +151,7 @@ func TestBridgeStartDeletesWebhookBeforePolling(t *testing.T) { | |||
| 151 | 151 | mu.Unlock() | |
| 152 | 152 | switch endpoint { | |
| 153 | 153 | case "getMe": | |
| 154 | - | return telegramResponse(`{"ok":true,"result":{"id":1,"is_bot":true,"username":"zot_test"}}`), nil | |
| 154 | + | return telegramResponse(`{"ok":true,"result":{"id":1,"is_bot":true,"username":"zut_test"}}`), nil | |
| 155 | 155 | case "deleteWebhook": | |
| 156 | 156 | return telegramResponse(`{"ok":true,"result":true}`), nil | |
| 157 | 157 | case "getUpdates": | |
| @@ -165,7 +165,7 @@ func TestBridgeStartDeletesWebhookBeforePolling(t *testing.T) { | |||
| 165 | 165 | })) | |
| 166 | 166 | bridge := &Bridge{ | |
| 167 | 167 | Client: client, | |
| 168 | - | Config: Config{BotToken: "test", BotID: 1, BotUsername: "zot_test"}, | |
| 168 | + | Config: Config{BotToken: "test", BotID: 1, BotUsername: "zut_test"}, | |
| 169 | 169 | Save: func(Config) error { return nil }, | |
| 170 | 170 | Host: testBridgeHost{}, | |
| 171 | 171 | } | |
| @@ -201,7 +201,7 @@ func TestAdapterRunFailsBeforePollingWhenWebhookRemovalFails(t *testing.T) { | |||
| 201 | 201 | endpoint := req.URL.Path[strings.LastIndex(req.URL.Path, "/")+1:] | |
| 202 | 202 | calls.WriteString(endpoint + "\n") | |
| 203 | 203 | if endpoint == "getMe" { | |
| 204 | - | return telegramResponse(`{"ok":true,"result":{"id":1,"is_bot":true,"username":"zot_test"}}`), nil | |
| 204 | + | return telegramResponse(`{"ok":true,"result":{"id":1,"is_bot":true,"username":"zut_test"}}`), nil | |
| 205 | 205 | } | |
| 206 | 206 | return telegramResponse(`{"ok":false,"description":"webhook removal denied","result":false}`), nil | |
| 207 | 207 | })) | |
M packages/agent/modes/telegram/bridge.go +14 -14
| @@ -7,8 +7,8 @@ import ( | |||
| 7 | 7 | "sync" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/agent/modes/bot" | |
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/agent/modes/bot" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // Host is the small interface the Bridge calls back into the TUI | |
| @@ -37,7 +37,7 @@ type Host interface { | |||
| 37 | 37 | // Host's running agent, then mirrors the agent's final assistant | |
| 38 | 38 | // text back to the paired Telegram user. One bridge per Interactive | |
| 39 | 39 | // instance; created on /telegram connect, stopped on /telegram | |
| 40 | - | // disconnect or zot exit. | |
| 40 | + | // disconnect or zut exit. | |
| 41 | 41 | type Bridge struct { | |
| 42 | 42 | Client *Client | |
| 43 | 43 | Config Config | |
| @@ -52,10 +52,10 @@ type Bridge struct { | |||
| 52 | 52 | workingCancel context.CancelFunc | |
| 53 | 53 | ||
| 54 | 54 | // nextReplyFromTelegram is set when the next assistant reply | |
| 55 | - | // should be sent bare (no "zot: " prefix) because the turn was | |
| 55 | + | // should be sent bare (no "zut: " prefix) because the turn was | |
| 56 | 56 | // initiated by a Telegram DM. The flag clears as soon as the | |
| 57 | 57 | // reply is flushed. TUI-originated turns leave the flag false | |
| 58 | - | // so the reply is tagged "zot: " for clarity on the two-sided | |
| 58 | + | // so the reply is tagged "zut: " for clarity on the two-sided | |
| 59 | 59 | // transcript. | |
| 60 | 60 | nextReplyFromTelegram bool | |
| 61 | 61 | } | |
| @@ -63,7 +63,7 @@ type Bridge struct { | |||
| 63 | 63 | // State is the snapshot /telegram status reports. | |
| 64 | 64 | type State struct { | |
| 65 | 65 | Running bool | |
| 66 | - | Username string // bot username, e.g. "zotbot" | |
| 66 | + | Username string // bot username, e.g. "zutbot" | |
| 67 | 67 | PairedID int64 // 0 when no user has claimed the bot yet | |
| 68 | 68 | } | |
| 69 | 69 | ||
| @@ -103,7 +103,7 @@ func (b *Bridge) Start(parent context.Context) error { | |||
| 103 | 103 | } | |
| 104 | 104 | if b.Config.BotToken == "" { | |
| 105 | 105 | b.mu.Unlock() | |
| 106 | - | return fmt.Errorf("no bot token configured; run `zot telegram-bot setup` first") | |
| 106 | + | return fmt.Errorf("no bot token configured; run `zut telegram-bot setup` first") | |
| 107 | 107 | } | |
| 108 | 108 | b.mu.Unlock() | |
| 109 | 109 | ||
| @@ -160,14 +160,14 @@ func (b *Bridge) Stop() { | |||
| 160 | 160 | // OnAssistantText should be called by the TUI with the assistant's | |
| 161 | 161 | // final visible text for each turn. The bridge forwards it to the | |
| 162 | 162 | // paired chat in message-sized chunks. Prefix depends on which | |
| 163 | - | // side initiated the turn: TUI-originated turns get "zot: " so the | |
| 164 | - | // two-sided transcript reads naturally ("you: ..." / "zot: ..."), | |
| 163 | + | // side initiated the turn: TUI-originated turns get "zut: " so the | |
| 164 | + | // two-sided transcript reads naturally ("you: ..." / "zut: ..."), | |
| 165 | 165 | // while Telegram-originated turns send bare text (the user's own | |
| 166 | - | // bubble is already on-screen, a "zot: " prefix would just add | |
| 166 | + | // bubble is already on-screen, a "zut: " prefix would just add | |
| 167 | 167 | // visual noise to a plain back-and-forth). | |
| 168 | 168 | func (b *Bridge) OnAssistantText(text string) { | |
| 169 | 169 | b.mu.Lock() | |
| 170 | - | // prefix := "zot: " | |
| 170 | + | // prefix := "zut: " | |
| 171 | 171 | prefix := "" | |
| 172 | 172 | workingCancel := b.workingCancel | |
| 173 | 173 | b.workingCancel = nil | |
| @@ -182,7 +182,7 @@ func (b *Bridge) OnAssistantText(text string) { | |||
| 182 | 182 | b.sendToPaired(text, prefix) | |
| 183 | 183 | } | |
| 184 | 184 | ||
| 185 | - | // OnUserTyped mirrors a message the user typed in the zot TUI into | |
| 185 | + | // OnUserTyped mirrors a message the user typed in the zut TUI into | |
| 186 | 186 | // the paired Telegram chat, tagged "you:" so the Telegram thread | |
| 187 | 187 | // stays a complete record of the conversation (both TUI-originated | |
| 188 | 188 | // and Telegram-originated turns). Messages sent from Telegram | |
| @@ -362,7 +362,7 @@ func (b *Bridge) handleUpdate(ctx context.Context, u Update) { | |||
| 362 | 362 | _ = b.Save(b.Config) | |
| 363 | 363 | b.mu.Unlock() | |
| 364 | 364 | _ = b.Client.SendMessage(ctx, msg.Chat.ID, | |
| 365 | - | fmt.Sprintf("paired with @%s. messages you send here now mirror into the zot tui.", msg.From.Username), | |
| 365 | + | fmt.Sprintf("paired with @%s. messages you send here now mirror into the zut tui.", msg.From.Username), | |
| 366 | 366 | msg.MessageID) | |
| 367 | 367 | b.Host.Notify("success", fmt.Sprintf("telegram paired with user %d (@%s)", msg.From.ID, msg.From.Username)) | |
| 368 | 368 | return | |
| @@ -391,7 +391,7 @@ func (b *Bridge) handleUpdate(ctx context.Context, u Update) { | |||
| 391 | 391 | switch command { | |
| 392 | 392 | case bot.CmdStart, bot.CmdHelp: | |
| 393 | 393 | _ = b.Client.SendMessage(ctx, msg.Chat.ID, | |
| 394 | - | "mirror is active. send me a message and it'll be forwarded to the zot tui. commands: /status, /stop, or plain stop.", | |
| 394 | + | "mirror is active. send me a message and it'll be forwarded to the zut tui. commands: /status, /stop, or plain stop.", | |
| 395 | 395 | msg.MessageID) | |
| 396 | 396 | case bot.CmdStatus: | |
| 397 | 397 | _ = b.Client.SendMessage(ctx, msg.Chat.ID, b.Host.Status(), msg.MessageID) | |
M packages/agent/modes/telegram/commands.go +1 -1
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | package telegram | |
| 2 | 2 | ||
| 3 | - | import "github.com/patriceckhart/zot/packages/agent/modes/bot" | |
| 3 | + | import "github.com/bnema/zut/packages/agent/modes/bot" | |
| 4 | 4 | ||
| 5 | 5 | // isStopCommand is a shim to bot.IsStopCommand for backward compatibility. | |
| 6 | 6 | var isStopCommand = bot.IsStopCommand | |
M packages/agent/modes/telegram/config.go +9 -9
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // Package telegram implements zot's Telegram bot bridge. | |
| 1 | + | // Package telegram implements zut's Telegram bot bridge. | |
| 2 | 2 | // | |
| 3 | 3 | // It runs in-process, polling Telegram for DMs and forwarding them to | |
| 4 | 4 | // a core.Agent. Responses stream back as Telegram messages. Images | |
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | // provider.ImageBlock, so vision-capable models see them the same way | |
| 7 | 7 | // they would via drag-and-drop in the TUI. | |
| 8 | 8 | // | |
| 9 | - | // State (bot token, allowed user id) lives in $ZOT_HOME/bot.json. | |
| 9 | + | // State (bot token, allowed user id) lives in $ZUT_HOME/bot.json. | |
| 10 | 10 | package telegram | |
| 11 | 11 | ||
| 12 | 12 | import ( | |
| @@ -26,14 +26,14 @@ type Config struct { | |||
| 26 | 26 | } | |
| 27 | 27 | ||
| 28 | 28 | // ConfigPath returns the path to bot.json. | |
| 29 | - | func ConfigPath(zotHome string) string { | |
| 30 | - | return filepath.Join(zotHome, "bot.json") | |
| 29 | + | func ConfigPath(zutHome string) string { | |
| 30 | + | return filepath.Join(zutHome, "bot.json") | |
| 31 | 31 | } | |
| 32 | 32 | ||
| 33 | 33 | // LoadConfig reads bot.json, returning a zero Config if it doesn't exist. | |
| 34 | - | func LoadConfig(zotHome string) (Config, error) { | |
| 34 | + | func LoadConfig(zutHome string) (Config, error) { | |
| 35 | 35 | var c Config | |
| 36 | - | b, err := os.ReadFile(ConfigPath(zotHome)) | |
| 36 | + | b, err := os.ReadFile(ConfigPath(zutHome)) | |
| 37 | 37 | if errors.Is(err, os.ErrNotExist) { | |
| 38 | 38 | return c, nil | |
| 39 | 39 | } | |
| @@ -47,15 +47,15 @@ func LoadConfig(zotHome string) (Config, error) { | |||
| 47 | 47 | } | |
| 48 | 48 | ||
| 49 | 49 | // SaveConfig writes bot.json atomically. | |
| 50 | - | func SaveConfig(zotHome string, c Config) error { | |
| 51 | - | if err := os.MkdirAll(zotHome, 0o755); err != nil { | |
| 50 | + | func SaveConfig(zutHome string, c Config) error { | |
| 51 | + | if err := os.MkdirAll(zutHome, 0o755); err != nil { | |
| 52 | 52 | return err | |
| 53 | 53 | } | |
| 54 | 54 | b, err := json.MarshalIndent(c, "", " ") | |
| 55 | 55 | if err != nil { | |
| 56 | 56 | return err | |
| 57 | 57 | } | |
| 58 | - | path := ConfigPath(zotHome) | |
| 58 | + | path := ConfigPath(zutHome) | |
| 59 | 59 | tmp := path + ".tmp" | |
| 60 | 60 | if err := os.WriteFile(tmp, b, 0o600); err != nil { | |
| 61 | 61 | return err | |
M packages/agent/modes/telegram/daemon.go +8 -8
| @@ -4,30 +4,30 @@ import ( | |||
| 4 | 4 | "path/filepath" | |
| 5 | 5 | "time" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/agent/modes/bot" | |
| 7 | + | "github.com/bnema/zut/packages/agent/modes/bot" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | // PIDPath returns the location of the bot's pid file. | |
| 11 | - | func PIDPath(zotHome string) string { return filepath.Join(zotHome, "bot.pid") } | |
| 11 | + | func PIDPath(zutHome string) string { return filepath.Join(zutHome, "bot.pid") } | |
| 12 | 12 | ||
| 13 | 13 | // LogPath returns the location of the bot's log file (stdout+stderr | |
| 14 | - | // from a detached `zot bot start`). | |
| 15 | - | func LogPath(zotHome string) string { return filepath.Join(zotHome, "logs", "bot.log") } | |
| 14 | + | // from a detached `zut bot start`). | |
| 15 | + | func LogPath(zutHome string) string { return filepath.Join(zutHome, "logs", "bot.log") } | |
| 16 | 16 | ||
| 17 | 17 | // WritePID persists pid to bot.pid. Overwrites any existing file. | |
| 18 | - | func WritePID(zotHome string, pid int) error { return bot.WritePIDFile(PIDPath(zotHome), pid) } | |
| 18 | + | func WritePID(zutHome string, pid int) error { return bot.WritePIDFile(PIDPath(zutHome), pid) } | |
| 19 | 19 | ||
| 20 | 20 | // ReadPID returns the pid stored in bot.pid, or 0 if the file doesn't | |
| 21 | 21 | // exist. Returns an error for any other read/parse failure. | |
| 22 | - | func ReadPID(zotHome string) (int, error) { return bot.ReadPIDFile(PIDPath(zotHome)) } | |
| 22 | + | func ReadPID(zutHome string) (int, error) { return bot.ReadPIDFile(PIDPath(zutHome)) } | |
| 23 | 23 | ||
| 24 | 24 | // RemovePID deletes the pid file if it exists. | |
| 25 | - | func RemovePID(zotHome string) error { return bot.RemovePIDFile(PIDPath(zotHome)) } | |
| 25 | + | func RemovePID(zutHome string) error { return bot.RemovePIDFile(PIDPath(zutHome)) } | |
| 26 | 26 | ||
| 27 | 27 | // IsRunning returns (pid, true) if a live process with the recorded | |
| 28 | 28 | // pid exists, or (pid, false) if the pid file points to a dead process. | |
| 29 | 29 | // Stale pid files are left in place; the caller may remove them. | |
| 30 | - | func IsRunning(zotHome string) (int, bool, error) { return bot.IsRunningAt(PIDPath(zotHome)) } | |
| 30 | + | func IsRunning(zutHome string) (int, bool, error) { return bot.IsRunningAt(PIDPath(zutHome)) } | |
| 31 | 31 | ||
| 32 | 32 | // StopProcess asks pid to exit and waits up to graceful for it to stop, | |
| 33 | 33 | // then escalates to a forced kill. Returns nil if the process is gone. | |
M packages/agent/modes/telegram/status.go +1 -1
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | package telegram | |
| 2 | 2 | ||
| 3 | - | import "github.com/patriceckhart/zot/packages/agent/modes/bot" | |
| 3 | + | import "github.com/bnema/zut/packages/agent/modes/bot" | |
| 4 | 4 | ||
| 5 | 5 | // StatusSnapshot is an alias for bot.StatusSnapshot for backward compatibility. | |
| 6 | 6 | type StatusSnapshot = bot.StatusSnapshot | |
M packages/agent/modes/telegram/status_test.go +3 -3
| @@ -4,14 +4,14 @@ import ( | |||
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 7 | + | "github.com/bnema/zut/packages/provider" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestFormatStatusIncludesModelUsageContextAndCWD(t *testing.T) { | |
| 11 | 11 | got := FormatStatus(StatusSnapshot{ | |
| 12 | 12 | Provider: "openai", | |
| 13 | 13 | Model: "gpt-5.5", | |
| 14 | - | CWD: "/tmp/zot", | |
| 14 | + | CWD: "/tmp/zut", | |
| 15 | 15 | Usage: provider.Usage{InputTokens: 961_000, OutputTokens: 10_000, CacheReadTokens: 770_000, CostUSD: 2.749}, | |
| 16 | 16 | Subscription: true, | |
| 17 | 17 | ContextUsed: 44_800, | |
| @@ -29,7 +29,7 @@ func TestFormatStatusIncludesModelUsageContextAndCWD(t *testing.T) { | |||
| 29 | 29 | "11.2%/400k", | |
| 30 | 30 | "state: working", | |
| 31 | 31 | "queued: 2", | |
| 32 | - | "cwd: /tmp/zot", | |
| 32 | + | "cwd: /tmp/zut", | |
| 33 | 33 | } | |
| 34 | 34 | for _, want := range wants { | |
| 35 | 35 | if !strings.Contains(got, want) { | |
M packages/agent/modes/telegram_dialog.go +1 -1
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | package modes | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 4 | + | "github.com/bnema/zut/packages/tui" | |
| 5 | 5 | ) | |
| 6 | 6 | ||
| 7 | 7 | // telegramDialog is the picker shown when the user runs `/telegram` | |
M packages/agent/modes/theme_setting_test.go +1 -1
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 6 | + | "github.com/bnema/zut/packages/tui" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestSettingsDialogOffersInheritedTheme(t *testing.T) { | |
M packages/agent/modes/update_banner.go +3 -3
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 6 | + | "github.com/bnema/zut/packages/tui" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | // UpdateInfo mirrors agent.UpdateInfo without the import cycle. The | |
| @@ -32,10 +32,10 @@ func renderUpdateBanner(th tui.Theme, info UpdateInfo, width int) []string { | |||
| 32 | 32 | } | |
| 33 | 33 | out = append(out, "") | |
| 34 | 34 | ||
| 35 | - | title := fmt.Sprintf("zot %s is available (you're on %s).", info.Latest, info.Current) | |
| 35 | + | title := fmt.Sprintf("zut %s is available (you're on %s).", info.Latest, info.Current) | |
| 36 | 36 | out = append(out, " "+th.FGColor(color, tui.Bold(title))) | |
| 37 | 37 | out = append(out, "") | |
| 38 | - | out = append(out, " "+th.FGColor(th.Muted, "run: ")+th.FGColor(color, "zot update")) | |
| 38 | + | out = append(out, " "+th.FGColor(th.Muted, "run: ")+th.FGColor(color, "zut update")) | |
| 39 | 39 | ||
| 40 | 40 | if info.URL != "" { | |
| 41 | 41 | out = append(out, " "+th.FGColor(th.Muted, "changelog: ")+th.FGColor(color, info.URL)) | |
M packages/agent/modes/update_banner_test.go +1 -1
| @@ -3,7 +3,7 @@ package modes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 6 | + | "github.com/bnema/zut/packages/tui" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestRenderUpdateBannerOmitsUnavailableUpdate(t *testing.T) { | |
M packages/agent/modes/welcome.go +6 -6
| @@ -1,18 +1,18 @@ | |||
| 1 | 1 | package modes | |
| 2 | 2 | ||
| 3 | - | import "github.com/patriceckhart/zot/packages/tui" | |
| 3 | + | import "github.com/bnema/zut/packages/tui" | |
| 4 | 4 | ||
| 5 | 5 | // welcomeBanner returns the intro text shown at the top of an empty chat. | |
| 6 | - | // It uses the `zot` label color (same as the assistant) for consistency. | |
| 6 | + | // It uses the `zut` label color (same as the assistant) for consistency. | |
| 7 | 7 | // | |
| 8 | 8 | // When version is non-empty AND showVersion is true, the headline | |
| 9 | - | // reads "i'm zot (vX.Y.Z). ..." so users see which build they're on | |
| 10 | - | // the moment zot starts. After welcomeVersionDuration the caller | |
| 9 | + | // reads "i'm zut (vX.Y.Z). ..." so users see which build they're on | |
| 10 | + | // the moment zut starts. After welcomeVersionDuration the caller | |
| 11 | 11 | // flips showVersion off and the headline reverts to plain text. | |
| 12 | 12 | func welcomeBanner(th tui.Theme, version string, showVersion bool) []string { | |
| 13 | - | text := "zot. yet another coding agent harness." | |
| 13 | + | text := "zut. yet another coding agent harness." | |
| 14 | 14 | if showVersion && version != "" { | |
| 15 | - | text = "zot (" + version + "). yet another coding agent harness." | |
| 15 | + | text = "zut (" + version + "). yet another coding agent harness." | |
| 16 | 16 | } | |
| 17 | 17 | headline := th.AccentBar(th.Assistant) + th.FGColor(th.Assistant, tui.Bold(text)) | |
| 18 | 18 | return []string{ | |
M packages/agent/ponytail_test.go +8 -8
| @@ -54,7 +54,7 @@ func TestResolvePonytailModeDefaultAndExplicitDisable(t *testing.T) { | |||
| 54 | 54 | for _, tc := range cases { | |
| 55 | 55 | t.Run(tc.name, func(t *testing.T) { | |
| 56 | 56 | root := t.TempDir() | |
| 57 | - | t.Setenv("ZOT_HOME", filepath.Join(root, "zot-home")) | |
| 57 | + | t.Setenv("ZUT_HOME", filepath.Join(root, "zut-home")) | |
| 58 | 58 | t.Setenv("HOME", filepath.Join(root, "home")) | |
| 59 | 59 | if tc.disabled { | |
| 60 | 60 | disabled := false | |
| @@ -94,13 +94,13 @@ func btoi(value bool) int { | |||
| 94 | 94 | ||
| 95 | 95 | func TestResolvePonytailModeRetainsAddendumWithSystemFile(t *testing.T) { | |
| 96 | 96 | root := t.TempDir() | |
| 97 | - | zotHome := filepath.Join(root, "zot-home") | |
| 98 | - | t.Setenv("ZOT_HOME", zotHome) | |
| 97 | + | zutHome := filepath.Join(root, "zut-home") | |
| 98 | + | t.Setenv("ZUT_HOME", zutHome) | |
| 99 | 99 | t.Setenv("HOME", filepath.Join(root, "home")) | |
| 100 | - | if err := os.MkdirAll(zotHome, 0o755); err != nil { | |
| 100 | + | if err := os.MkdirAll(zutHome, 0o755); err != nil { | |
| 101 | 101 | t.Fatal(err) | |
| 102 | 102 | } | |
| 103 | - | if err := os.WriteFile(filepath.Join(zotHome, "SYSTEM.md"), []byte("custom system identity"), 0o600); err != nil { | |
| 103 | + | if err := os.WriteFile(filepath.Join(zutHome, "SYSTEM.md"), []byte("custom system identity"), 0o600); err != nil { | |
| 104 | 104 | t.Fatal(err) | |
| 105 | 105 | } | |
| 106 | 106 | ||
| @@ -125,7 +125,7 @@ func TestResolvePonytailModeRetainsAddendumWithSystemFile(t *testing.T) { | |||
| 125 | 125 | ||
| 126 | 126 | func TestResolvePonytailModePrecedesExplicitAppendPrompt(t *testing.T) { | |
| 127 | 127 | root := t.TempDir() | |
| 128 | - | t.Setenv("ZOT_HOME", filepath.Join(root, "zot-home")) | |
| 128 | + | t.Setenv("ZUT_HOME", filepath.Join(root, "zut-home")) | |
| 129 | 129 | t.Setenv("HOME", filepath.Join(root, "home")) | |
| 130 | 130 | ||
| 131 | 131 | const explicit = "explicit invocation guidance" | |
| @@ -150,7 +150,7 @@ func TestResolvePonytailModePrecedesExplicitAppendPrompt(t *testing.T) { | |||
| 150 | 150 | ||
| 151 | 151 | func TestResolvePonytailModeAppliesAcrossHeadlessModesAndCustomIdentity(t *testing.T) { | |
| 152 | 152 | root := t.TempDir() | |
| 153 | - | t.Setenv("ZOT_HOME", filepath.Join(root, "zot-home")) | |
| 153 | + | t.Setenv("ZUT_HOME", filepath.Join(root, "zut-home")) | |
| 154 | 154 | t.Setenv("HOME", filepath.Join(root, "home")) | |
| 155 | 155 | ||
| 156 | 156 | modes := []Mode{"", ModePrint, ModeStream, ModeJSON, ModeRPC, ModeSubagentWorker} | |
| @@ -178,7 +178,7 @@ func TestResolvePonytailModeAppliesAcrossHeadlessModesAndCustomIdentity(t *testi | |||
| 178 | 178 | } | |
| 179 | 179 | ||
| 180 | 180 | func TestConfigSettingsStorePersistsPonytailWithoutChangingKnownFields(t *testing.T) { | |
| 181 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 181 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 182 | 182 | if err := SaveConfig(Config{Theme: "dark", Provider: "ollama", Model: "local"}); err != nil { | |
| 183 | 183 | t.Fatal(err) | |
| 184 | 184 | } | |
M packages/agent/print_stats_test.go +1 -1
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "testing" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func TestWritePrintStats(t *testing.T) { | |
M packages/agent/rpc.go +12 -12
| @@ -12,12 +12,12 @@ import ( | |||
| 12 | 12 | "sync" | |
| 13 | 13 | "time" | |
| 14 | 14 | ||
| 15 | - | "github.com/patriceckhart/zot/packages/agent/extensions" | |
| 16 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 17 | - | "github.com/patriceckhart/zot/packages/agent/modes" | |
| 18 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 19 | - | "github.com/patriceckhart/zot/packages/core" | |
| 20 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 15 | + | "github.com/bnema/zut/packages/agent/extensions" | |
| 16 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 17 | + | "github.com/bnema/zut/packages/agent/modes" | |
| 18 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 19 | + | "github.com/bnema/zut/packages/core" | |
| 20 | + | "github.com/bnema/zut/packages/provider" | |
| 21 | 21 | ) | |
| 22 | 22 | ||
| 23 | 23 | // runRPCMode implements the JSON-over-stdin/stdout RPC protocol. | |
| @@ -42,7 +42,7 @@ import ( | |||
| 42 | 42 | // ext_widget with extension/id/position/title/lines, and ext_widget_clear | |
| 43 | 43 | // with extension/id when loaded extensions publish persistent UI state. | |
| 44 | 44 | // | |
| 45 | - | // Auth: if $ZOTCORE_RPC_TOKEN is set, the first command must be | |
| 45 | + | // Auth: if $ZUTCORE_RPC_TOKEN is set, the first command must be | |
| 46 | 46 | // {"type":"hello","token":"..."} or the connection is closed. | |
| 47 | 47 | func runRPCMode(ctx context.Context, args Args, version string) error { | |
| 48 | 48 | if args.NoYolo { | |
| @@ -69,7 +69,7 @@ func runRPCMode(ctx context.Context, args Args, version string) error { | |||
| 69 | 69 | // host-hooks integration. Notify/Display calls from extensions | |
| 70 | 70 | // emit RPC events instead of TUI lines so any consumer can react. | |
| 71 | 71 | extHooks := &rpcExtHooks{server: server} | |
| 72 | - | extMgr := extensions.New(ZotHome(), r.CWD, version, r.Provider, r.Model, extHooks) | |
| 72 | + | extMgr := extensions.New(ZutHome(), r.CWD, version, r.Provider, r.Model, extHooks) | |
| 73 | 73 | for _, e := range extMgr.LoadExplicit(ctx, args.Exts) { | |
| 74 | 74 | fmt.Fprintln(os.Stderr, "extension load:", e) | |
| 75 | 75 | } | |
| @@ -228,7 +228,7 @@ type rpcServer struct { | |||
| 228 | 228 | ||
| 229 | 229 | // inFlight tracks long-running command goroutines so run() can | |
| 230 | 230 | // wait for them before returning when stdin closes. Without this, | |
| 231 | - | // piping a single 'prompt' command into 'zot rpc' would race the | |
| 231 | + | // piping a single 'prompt' command into 'zut rpc' would race the | |
| 232 | 232 | // process exit against the agent loop and the prompt would never | |
| 233 | 233 | // produce output. | |
| 234 | 234 | inFlight sync.WaitGroup | |
| @@ -236,10 +236,10 @@ type rpcServer struct { | |||
| 236 | 236 | ||
| 237 | 237 | // run reads NDJSON commands from in and dispatches them. Returns when | |
| 238 | 238 | // in is closed AND every in-flight long-running command (prompt / | |
| 239 | - | // compact) has finished, so a quick `echo cmd | zot rpc` invocation | |
| 239 | + | // compact) has finished, so a quick `echo cmd | zut rpc` invocation | |
| 240 | 240 | // still produces full output before the process exits. | |
| 241 | 241 | func (s *rpcServer) run(in io.Reader) error { | |
| 242 | - | requireToken := os.Getenv("ZOTCORE_RPC_TOKEN") != "" | |
| 242 | + | requireToken := os.Getenv("ZUTCORE_RPC_TOKEN") != "" | |
| 243 | 243 | s.writeMu.Lock() | |
| 244 | 244 | s.started = true | |
| 245 | 245 | s.authed = !requireToken | |
| @@ -272,7 +272,7 @@ func (s *rpcServer) run(in io.Reader) error { | |||
| 272 | 272 | Token string `json:"token"` | |
| 273 | 273 | } | |
| 274 | 274 | _ = json.Unmarshal([]byte(line), &hello) | |
| 275 | - | if hello.Token != os.Getenv("ZOTCORE_RPC_TOKEN") { | |
| 275 | + | if hello.Token != os.Getenv("ZUTCORE_RPC_TOKEN") { | |
| 276 | 276 | s.writeError(head.ID, head.Type, "invalid token") | |
| 277 | 277 | return fmt.Errorf("rpc: bad auth token") | |
| 278 | 278 | } | |
M packages/agent/rpc_alert_test.go +2 -2
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "testing" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/extproto" | |
| 9 | + | "github.com/bnema/zut/packages/agent/extproto" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | func TestRPCExtAlertIsJSONOnly(t *testing.T) { | |
| @@ -27,7 +27,7 @@ func TestRPCExtAlertIsJSONOnly(t *testing.T) { | |||
| 27 | 27 | } | |
| 28 | 28 | ||
| 29 | 29 | func TestRPCExtAlertWaitsForAuthentication(t *testing.T) { | |
| 30 | - | t.Setenv("ZOTCORE_RPC_TOKEN", "synthetic-token") | |
| 30 | + | t.Setenv("ZUTCORE_RPC_TOKEN", "synthetic-token") | |
| 31 | 31 | ||
| 32 | 32 | var out bytes.Buffer | |
| 33 | 33 | server := &rpcServer{out: &out, provider: "test", model: "test-model", version: "test-version"} | |
M packages/agent/rpc_reasoning_test.go +1 -1
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/core" | |
| 8 | + | "github.com/bnema/zut/packages/core" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | func TestRPCSetReasoningMax(t *testing.T) { | |
M packages/agent/sdk/reasoning_test.go +1 -1
| @@ -3,7 +3,7 @@ package sdk | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/core" | |
| 6 | + | "github.com/bnema/zut/packages/core" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestRuntimeSetReasoningMax(t *testing.T) { | |
M packages/agent/sdk/sdk.go +8 -8
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // Package sdk is the public Go SDK for embedding the zot agent | |
| 1 | + | // Package sdk is the public Go SDK for embedding the zut agent | |
| 2 | 2 | // runtime in third-party programs. It is the only stable, importable | |
| 3 | 3 | // surface the project exposes; everything under internal/ is subject | |
| 4 | 4 | // to change without notice. | |
| @@ -19,7 +19,7 @@ | |||
| 19 | 19 | // Runtime per project / cwd. The Cancel call interrupts the active | |
| 20 | 20 | // prompt; subsequent prompts work normally. | |
| 21 | 21 | // | |
| 22 | - | // For a non-Go consumer, run `zot rpc` and speak the same JSON | |
| 22 | + | // For a non-Go consumer, run `zut rpc` and speak the same JSON | |
| 23 | 23 | // schema over stdin/stdout. See docs/rpc.md. | |
| 24 | 24 | package sdk | |
| 25 | 25 | ||
| @@ -29,14 +29,14 @@ import ( | |||
| 29 | 29 | "fmt" | |
| 30 | 30 | "sync" | |
| 31 | 31 | ||
| 32 | - | "github.com/patriceckhart/zot/packages/agent" | |
| 33 | - | agenttools "github.com/patriceckhart/zot/packages/agent/tools" | |
| 34 | - | "github.com/patriceckhart/zot/packages/core" | |
| 35 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 32 | + | "github.com/bnema/zut/packages/agent" | |
| 33 | + | agenttools "github.com/bnema/zut/packages/agent/tools" | |
| 34 | + | "github.com/bnema/zut/packages/core" | |
| 35 | + | "github.com/bnema/zut/packages/provider" | |
| 36 | 36 | ) | |
| 37 | 37 | ||
| 38 | 38 | // Config configures a Runtime. All fields are optional; sensible | |
| 39 | - | // defaults are read from $ZOT_HOME/config.json, env vars, and the | |
| 39 | + | // defaults are read from $ZUT_HOME/config.json, env vars, and the | |
| 40 | 40 | // resolver chain (the same one the cli uses). | |
| 41 | 41 | type Config struct { | |
| 42 | 42 | // Provider is "anthropic" or "openai". Empty = use the user's | |
| @@ -87,7 +87,7 @@ type Config struct { | |||
| 87 | 87 | Lock bool | |
| 88 | 88 | } | |
| 89 | 89 | ||
| 90 | - | // Runtime is one zot agent session. Safe for use from one goroutine | |
| 90 | + | // Runtime is one zut agent session. Safe for use from one goroutine | |
| 91 | 91 | // at a time per Runtime; create separate Runtimes for parallel work. | |
| 92 | 92 | type Runtime struct { | |
| 93 | 93 | mu sync.Mutex | |
M packages/agent/sdk/types.go +3 -3
| @@ -3,14 +3,14 @@ package sdk | |||
| 3 | 3 | import ( | |
| 4 | 4 | "encoding/json" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/core" | |
| 7 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 6 | + | "github.com/bnema/zut/packages/core" | |
| 7 | + | "github.com/bnema/zut/packages/provider" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | // Event is the unit emitted by Runtime.Prompt's channel. Type | |
| 11 | 11 | // identifies the event kind; remaining fields are populated based on | |
| 12 | 12 | // kind. The wire schema is the same as the JSON-RPC stream notifications | |
| 13 | - | // emitted by `zot rpc`, so consumers can share parsing code. | |
| 13 | + | // emitted by `zut rpc`, so consumers can share parsing code. | |
| 14 | 14 | type Event struct { | |
| 15 | 15 | Type string `json:"type"` | |
| 16 | 16 | ||
M packages/agent/settings_store.go +2 -2
| @@ -3,8 +3,8 @@ package agent | |||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 7 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 6 | + | "github.com/bnema/zut/packages/provider" | |
| 7 | + | "github.com/bnema/zut/packages/tui" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | type configSettingsStore struct{} | |
M packages/agent/settings_store_test.go +7 -7
| @@ -3,7 +3,7 @@ package agent | |||
| 3 | 3 | import "testing" | |
| 4 | 4 | ||
| 5 | 5 | func TestConfigSettingsStorePersistsShowInstructionsAtStartup(t *testing.T) { | |
| 6 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 6 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 7 | 7 | if err := SaveConfig(Config{Theme: "dark"}); err != nil { | |
| 8 | 8 | t.Fatal(err) | |
| 9 | 9 | } | |
| @@ -24,7 +24,7 @@ func TestConfigSettingsStorePersistsShowInstructionsAtStartup(t *testing.T) { | |||
| 24 | 24 | } | |
| 25 | 25 | ||
| 26 | 26 | func TestConfigSettingsStorePersistsTerminalAlerts(t *testing.T) { | |
| 27 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 27 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 28 | 28 | if err := SaveConfig(Config{Theme: "dark"}); err != nil { | |
| 29 | 29 | t.Fatal(err) | |
| 30 | 30 | } | |
| @@ -45,7 +45,7 @@ func TestConfigSettingsStorePersistsTerminalAlerts(t *testing.T) { | |||
| 45 | 45 | } | |
| 46 | 46 | ||
| 47 | 47 | func TestConfigSettingsStorePersistsTerminalTitle(t *testing.T) { | |
| 48 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 48 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 49 | 49 | if err := SaveConfig(Config{Theme: "dark"}); err != nil { | |
| 50 | 50 | t.Fatal(err) | |
| 51 | 51 | } | |
| @@ -66,7 +66,7 @@ func TestConfigSettingsStorePersistsTerminalTitle(t *testing.T) { | |||
| 66 | 66 | } | |
| 67 | 67 | ||
| 68 | 68 | func TestConfigSettingsStorePersistsJailByDefault(t *testing.T) { | |
| 69 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 69 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 70 | 70 | if err := SaveConfig(Config{Theme: "dark"}); err != nil { | |
| 71 | 71 | t.Fatal(err) | |
| 72 | 72 | } | |
| @@ -87,7 +87,7 @@ func TestConfigSettingsStorePersistsJailByDefault(t *testing.T) { | |||
| 87 | 87 | } | |
| 88 | 88 | ||
| 89 | 89 | func TestConfigSettingsStorePersistsAutoCompactThreshold(t *testing.T) { | |
| 90 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 90 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 91 | 91 | if err := SaveConfig(Config{Theme: "dark"}); err != nil { | |
| 92 | 92 | t.Fatal(err) | |
| 93 | 93 | } | |
| @@ -108,7 +108,7 @@ func TestConfigSettingsStorePersistsAutoCompactThreshold(t *testing.T) { | |||
| 108 | 108 | } | |
| 109 | 109 | ||
| 110 | 110 | func TestConfigSettingsStorePersistsLSPDefaults(t *testing.T) { | |
| 111 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 111 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 112 | 112 | if err := SaveConfig(Config{Theme: "dark"}); err != nil { | |
| 113 | 113 | t.Fatal(err) | |
| 114 | 114 | } | |
| @@ -163,7 +163,7 @@ func TestConfigLSPEnabledForDefaultsToTrue(t *testing.T) { | |||
| 163 | 163 | } | |
| 164 | 164 | ||
| 165 | 165 | func TestConfigSettingsStorePersistsInheritedTheme(t *testing.T) { | |
| 166 | - | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 166 | + | t.Setenv("ZUT_HOME", t.TempDir()) | |
| 167 | 167 | if err := SaveConfig(Config{Theme: "dark"}); err != nil { | |
| 168 | 168 | t.Fatal(err) | |
| 169 | 169 | } | |
M packages/agent/skills/builtin.go +1 -1
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "strings" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | - | // builtinFS holds the SKILL.md files zot ships with the binary. | |
| 10 | + | // builtinFS holds the SKILL.md files zut ships with the binary. | |
| 11 | 11 | // They appear in the catalogue as ordinary skills — same on-demand | |
| 12 | 12 | // load via the `skill` tool, same /skills picker — but never need | |
| 13 | 13 | // to be installed by the user. A user-installed skill with the same | |
R packages/agent/skills/builtin/write-zot-extension/SKILL.md → packages/agent/skills/builtin/write-zut-extension/SKILL.md +48 -44 renamed, 82% similar
| @@ -1,23 +1,23 @@ | |||
| 1 | 1 | --- | |
| 2 | - | name: write-zot-extension | |
| 3 | - | description: Help the user create a new zot extension (slash command, LLM tool, or guard) in any language. | |
| 2 | + | name: write-zut-extension | |
| 3 | + | description: Help the user create a new zut extension (slash command, LLM tool, or guard) in any language. | |
| 4 | 4 | --- | |
| 5 | 5 | ||
| 6 | - | # Writing a zot extension | |
| 6 | + | # Writing a zut extension | |
| 7 | 7 | ||
| 8 | - | Use this skill when the user asks for help building a zot extension — | |
| 8 | + | Use this skill when the user asks for help building a zut extension — | |
| 9 | 9 | a new slash command, a new tool the LLM can call, an audit hook, or | |
| 10 | 10 | a permission gate. Skim this whole skill first, then collaborate | |
| 11 | 11 | with the user on the specific extension they want. | |
| 12 | 12 | ||
| 13 | 13 | ## What an extension is | |
| 14 | 14 | ||
| 15 | - | A zot extension is **an external executable** that zot launches as a | |
| 15 | + | A zut extension is **an external executable** that zut launches as a | |
| 16 | 16 | subprocess and talks to over its stdin/stdout in newline-delimited | |
| 17 | 17 | JSON. It can be written in any language that can read/write JSON | |
| 18 | 18 | lines from stdio: Go, TypeScript (via tsx), Python, Rust, shell with | |
| 19 | 19 | jq, anything. Crash isolation is automatic; one bad extension never | |
| 20 | - | takes down zot. | |
| 20 | + | takes down zut. | |
| 21 | 21 | ||
| 22 | 22 | Three things an extension can do (any combination): | |
| 23 | 23 | ||
| @@ -27,7 +27,7 @@ Three things an extension can do (any combination): | |||
| 27 | 27 | (one-shot styled note in the chat), or a "noop". | |
| 28 | 28 | ||
| 29 | 29 | 2. **Tools** — register tools the LLM itself calls. Schema is | |
| 30 | - | JSON Schema; zot routes the model's `tool_call` to the | |
| 30 | + | JSON Schema; zut routes the model's `tool_call` to the | |
| 31 | 31 | extension's `tool_result`. Same lifecycle as built-in tools | |
| 32 | 32 | (read/write/edit/bash/lsp/skill). | |
| 33 | 33 | ||
| @@ -42,16 +42,16 @@ Three things an extension can do (any combination): | |||
| 42 | 42 | Each extension lives in its own directory: | |
| 43 | 43 | ||
| 44 | 44 | ``` | |
| 45 | - | ~/Library/Application Support/zot/extensions/<name>/ | |
| 45 | + | ~/Library/Application Support/zut/extensions/<name>/ | |
| 46 | 46 | ├── extension.json # manifest (required) | |
| 47 | 47 | └── <executable> # whatever exec points at | |
| 48 | 48 | ``` | |
| 49 | 49 | ||
| 50 | - | Or project-local: `<project>/.zot/extensions/<name>/`. Project-local | |
| 50 | + | Or project-local: `<project>/.zut/extensions/<name>/`. Project-local | |
| 51 | 51 | wins on name conflict. | |
| 52 | 52 | ||
| 53 | 53 | For ad-hoc use during development, skip the install step entirely | |
| 54 | - | and run `zot --ext PATH` (repeatable: `-e PATH -e PATH`). | |
| 54 | + | and run `zut --ext PATH` (repeatable: `-e PATH -e PATH`). | |
| 55 | 55 | ||
| 56 | 56 | ### Manifest | |
| 57 | 57 | ||
| @@ -68,7 +68,7 @@ and run `zot --ext PATH` (repeatable: `-e PATH -e PATH`). | |||
| 68 | 68 | ``` | |
| 69 | 69 | ||
| 70 | 70 | Field rules: | |
| 71 | - | - `name` (required, unique) — id zot uses internally; matches the | |
| 71 | + | - `name` (required, unique) — id zut uses internally; matches the | |
| 72 | 72 | hello frame. Slash commands & tools live in the same name space | |
| 73 | 73 | as built-ins; conflicts are silently shadowed by built-ins. | |
| 74 | 74 | - `exec` (required) — the executable path. Resolution: | |
| @@ -92,17 +92,18 @@ their responses. | |||
| 92 | 92 | The very first frame the extension sends is `hello`: | |
| 93 | 93 | ||
| 94 | 94 | ```json | |
| 95 | - | {"type":"hello","name":"weather","version":"1.0.0", | |
| 95 | + | {"type":"hello","protocol_version":2,"name":"weather","version":"1.0.0", | |
| 96 | 96 | "capabilities":["commands","tools"]} | |
| 97 | 97 | ``` | |
| 98 | 98 | ||
| 99 | 99 | Capabilities are advisory; current values are `commands`, `tools`, | |
| 100 | - | `events`, `alerts`, and `panels`. Send all that apply. | |
| 100 | + | `events`, `alerts`, and `panels`. Send all that apply. `protocol_version` | |
| 101 | + | is the major wire version; it is currently `2` and must match the host. | |
| 101 | 102 | ||
| 102 | - | zot replies with `hello_ack`: | |
| 103 | + | zut replies with `hello_ack`: | |
| 103 | 104 | ||
| 104 | 105 | ```json | |
| 105 | - | {"type":"hello_ack","protocol_version":1,"zot_version":"0.0.x", | |
| 106 | + | {"type":"hello_ack","protocol_version":2,"zut_version":"0.0.x", | |
| 106 | 107 | "provider":"anthropic","model":"claude-opus-4-7","cwd":"/path/to/project"} | |
| 107 | 108 | ``` | |
| 108 | 109 | ||
| @@ -110,7 +111,7 @@ zot replies with `hello_ack`: | |||
| 110 | 111 | ||
| 111 | 112 | The canonical startup order is `hello`, wait for `hello_ack`, send | |
| 112 | 113 | registration frames in any order, then send a single `ready` sentinel | |
| 113 | - | so zot can finalize the agent's tool registry: | |
| 114 | + | so zut can finalize the agent's tool registry: | |
| 114 | 115 | ||
| 115 | 116 | ```json | |
| 116 | 117 | {"type":"register_command","name":"weather","description":"current weather"} | |
| @@ -120,7 +121,7 @@ so zot can finalize the agent's tool registry: | |||
| 120 | 121 | {"type":"ready"} | |
| 121 | 122 | ``` | |
| 122 | 123 | ||
| 123 | - | If you don't send `ready`, zot's idle watchdog auto-treats you as | |
| 124 | + | If you don't send `ready`, zut's idle watchdog auto-treats you as | |
| 124 | 125 | ready after 250ms of no frames, but always send it explicitly when | |
| 125 | 126 | you can. Newer extensions on faster hosts shave that 250ms off. | |
| 126 | 127 | ||
| @@ -130,7 +131,7 @@ process itself runs from the extension directory, so do not use | |||
| 130 | 131 | ||
| 131 | 132 | ### Runtime frames | |
| 132 | 133 | ||
| 133 | - | **zot → extension:** | |
| 134 | + | **zut → extension:** | |
| 134 | 135 | ||
| 135 | 136 | ```json | |
| 136 | 137 | {"type":"command_invoked","id":"abc","name":"weather","args":"berlin"} | |
| @@ -141,7 +142,7 @@ process itself runs from the extension directory, so do not use | |||
| 141 | 142 | {"type":"shutdown"} | |
| 142 | 143 | ``` | |
| 143 | 144 | ||
| 144 | - | **extension → zot (replies + spontaneous notifications):** | |
| 145 | + | **extension → zut (replies + spontaneous notifications):** | |
| 145 | 146 | ||
| 146 | 147 | ```json | |
| 147 | 148 | {"type":"command_response","id":"abc","action":"prompt", | |
| @@ -171,7 +172,7 @@ stdout. | |||
| 171 | 172 | - `"insert"` — drop `insert` into the editor at the cursor | |
| 172 | 173 | - `"display"` — append `display` to chat as a one-shot note (no | |
| 173 | 174 | model call, not in transcript) | |
| 174 | - | - `"noop"` — handled internally; zot doesn't change the UI | |
| 175 | + | - `"noop"` — handled internally; zut doesn't change the UI | |
| 175 | 176 | ||
| 176 | 177 | `tool_result.content[]` blocks: `{"type":"text","text":"..."}` or | |
| 177 | 178 | `{"type":"image","mime_type":"image/png","data":"<base64>"}`. | |
| @@ -186,7 +187,7 @@ never stalls the agent. | |||
| 186 | 187 | stdout that isn't a JSON frame breaks the wire. The first stdout | |
| 187 | 188 | frame must be `hello`; do not send `notify`, `alert`, logs, or | |
| 188 | 189 | registration frames before that handshake starts. Use stderr for | |
| 189 | - | logs / debug output (zot captures stderr to `$ZOT_HOME/logs/ext-<name>.log`). | |
| 190 | + | logs / debug output (zut captures stderr to `$ZUT_HOME/logs/ext-<name>.log`). | |
| 190 | 191 | - **One JSON object per line.** No multi-line JSON. Always end | |
| 191 | 192 | every frame with `\n`. | |
| 192 | 193 | - **Flush after writing.** Most stdout writes are line-buffered when | |
| @@ -204,7 +205,7 @@ package main | |||
| 204 | 205 | ||
| 205 | 206 | import ( | |
| 206 | 207 | "encoding/json" | |
| 207 | - | "github.com/patriceckhart/zot/packages/agent/ext" | |
| 208 | + | "github.com/bnema/zut/packages/agent/ext" | |
| 208 | 209 | ) | |
| 209 | 210 | ||
| 210 | 211 | func main() { | |
| @@ -243,7 +244,7 @@ func main() { | |||
| 243 | 244 | Build: `go build -o weather .` | |
| 244 | 245 | ||
| 245 | 246 | `OnHello` is optional. Use it when configuration or registrations need | |
| 246 | - | host metadata such as `HostInfo.CWD`, `Provider`, `Model`, `ZotVersion`, | |
| 247 | + | host metadata such as `HostInfo.CWD`, `Provider`, `Model`, `ZutVersion`, | |
| 247 | 248 | `ExtensionDir`, or `DataDir`. The SDK sends `hello`, waits for | |
| 248 | 249 | `hello_ack`, runs `OnHello`, announces registrations, then sends `ready`. | |
| 249 | 250 | ||
| @@ -253,7 +254,7 @@ host metadata such as `HostInfo.CWD`, `Provider`, `Model`, `ZotVersion`, | |||
| 253 | 254 | ``` | |
| 254 | 255 | ||
| 255 | 256 | After the hello handshake completes inside `Run`, call `e.Alert` from a | |
| 256 | - | command, tool, or panel handler when the user needs to be drawn back to zot: | |
| 257 | + | command, tool, or panel handler when the user needs to be drawn back to zut: | |
| 257 | 258 | ||
| 258 | 259 | ```go | |
| 259 | 260 | e.Alert(ext.AlertRequest{Kind: ext.AlertKindBell, Reason: "question_ready"}) | |
| @@ -275,13 +276,14 @@ import { stderr, stdin, stdout } from "node:process"; | |||
| 275 | 276 | function send(o: object) { stdout.write(JSON.stringify(o) + "\n"); } | |
| 276 | 277 | function log(s: string) { stderr.write(`[scratchpad] ${s}\n`); } | |
| 277 | 278 | ||
| 278 | - | send({ type: "hello", name: "scratchpad", version: "1.0.0", | |
| 279 | + | send({ type: "hello", protocol_version: 2, name: "scratchpad", version: "1.0.0", | |
| 279 | 280 | capabilities: ["commands", "tools"] }); | |
| 280 | 281 | ||
| 281 | 282 | const rl = createInterface({ input: stdin, crlfDelay: Infinity }); | |
| 282 | 283 | rl.on("line", (line) => { | |
| 283 | 284 | const f = JSON.parse(line); | |
| 284 | 285 | if (f.type === "hello_ack") { | |
| 286 | + | if (f.protocol_version !== 2) throw new Error("unsupported extension protocol version"); | |
| 285 | 287 | // f.cwd is the user's project directory. | |
| 286 | 288 | send({ type: "register_command", name: "note", description: "append a note" }); | |
| 287 | 289 | send({ type: "register_tool", name: "read_notes", | |
| @@ -317,11 +319,13 @@ import json, sys | |||
| 317 | 319 | ||
| 318 | 320 | def emit(o): sys.stdout.write(json.dumps(o) + "\n"); sys.stdout.flush() | |
| 319 | 321 | ||
| 320 | - | emit({"type": "hello", "name": "hello-py", "version": "1.0.0", "capabilities": ["commands"]}) | |
| 322 | + | emit({"type": "hello", "protocol_version": 2, "name": "hello-py", "version": "1.0.0", "capabilities": ["commands"]}) | |
| 321 | 323 | ||
| 322 | 324 | for line in sys.stdin: | |
| 323 | 325 | msg = json.loads(line) | |
| 324 | 326 | if msg["type"] == "hello_ack": | |
| 327 | + | if msg.get("protocol_version") != 2: | |
| 328 | + | raise SystemExit("unsupported extension protocol version") | |
| 325 | 329 | # msg["cwd"] is the user's project directory. | |
| 326 | 330 | emit({"type": "register_command", "name": "hellopy", "description": "say hi (python)"}) | |
| 327 | 331 | emit({"type": "ready"}) | |
| @@ -338,24 +342,24 @@ for line in sys.stdin: | |||
| 338 | 342 | ## Install / dev workflow | |
| 339 | 343 | ||
| 340 | 344 | ```bash | |
| 341 | - | zot ext install ./weather # copy into $ZOT_HOME/extensions/ | |
| 342 | - | zot ext install --build=go ./weather # explicitly build local Go source | |
| 343 | - | zot --ext ./weather # run from disk for one zot session (no install) | |
| 344 | - | zot --ext . # cwd is the extension dir | |
| 345 | - | zot ext list # show installed extensions | |
| 346 | - | zot ext logs weather # cat the extension's stderr | |
| 347 | - | zot ext logs weather -f # tail it | |
| 348 | - | zot ext disable weather # keep installed but skip on launch | |
| 349 | - | zot ext enable weather | |
| 350 | - | zot ext remove weather | |
| 345 | + | zut ext install ./weather # copy into $ZUT_HOME/extensions/ | |
| 346 | + | zut ext install --build=go ./weather # explicitly build local Go source | |
| 347 | + | zut --ext ./weather # run from disk for one zut session (no install) | |
| 348 | + | zut --ext . # cwd is the extension dir | |
| 349 | + | zut ext list # show installed extensions | |
| 350 | + | zut ext logs weather # cat the extension's stderr | |
| 351 | + | zut ext logs weather -f # tail it | |
| 352 | + | zut ext disable weather # keep installed but skip on launch | |
| 353 | + | zut ext enable weather | |
| 354 | + | zut ext remove weather | |
| 351 | 355 | ``` | |
| 352 | 356 | ||
| 353 | 357 | For TS / Python extensions, no build step is needed — edit the source | |
| 354 | - | in place and relaunch zot. | |
| 358 | + | in place and relaunch zut. | |
| 355 | 359 | ||
| 356 | - | For Go, either run `zot ext install --build=go <path>` to explicitly build | |
| 360 | + | For Go, either run `zut ext install --build=go <path>` to explicitly build | |
| 357 | 361 | the local source into the staged install, or run `go build -o <name> .` | |
| 358 | - | first and then use `zot ext install <path>`. The default install never runs a | |
| 362 | + | first and then use `zut ext install <path>`. The default install never runs a | |
| 359 | 363 | build step; a missing local executable is rejected with an explicit builder | |
| 360 | 364 | hint instead of leaving a broken installed extension. The `--build=go` option | |
| 361 | 365 | is intentionally limited to local source paths and does not apply to git URLs. | |
| @@ -367,7 +371,7 @@ to see exactly what's happening on the wire: | |||
| 367 | 371 | ||
| 368 | 372 | ```bash | |
| 369 | 373 | { | |
| 370 | - | printf '%s\n' '{"type":"hello_ack","protocol_version":1,"zot_version":"x","provider":"a","model":"o","cwd":"/tmp"}' | |
| 374 | + | printf '%s\n' '{"type":"hello_ack","protocol_version":2,"zut_version":"x","provider":"a","model":"o","cwd":"/tmp"}' | |
| 371 | 375 | sleep 0.2 | |
| 372 | 376 | printf '%s\n' '{"type":"command_invoked","id":"1","name":"weather","args":"Berlin"}' | |
| 373 | 377 | sleep 0.5 | |
| @@ -376,7 +380,7 @@ to see exactly what's happening on the wire: | |||
| 376 | 380 | ``` | |
| 377 | 381 | ||
| 378 | 382 | Compare what comes out of stdout to the expected wire format. If a | |
| 379 | - | frame doesn't match what zot expects, it's discarded silently and | |
| 383 | + | frame doesn't match what zut expects, it's discarded silently and | |
| 380 | 384 | logged to `ext-<name>.log`. | |
| 381 | 385 | ||
| 382 | 386 | ## Process to follow with the user | |
| @@ -391,10 +395,10 @@ logged to `ext-<name>.log`. | |||
| 391 | 395 | if they prefer JS-flavored ergonomics; **Python** for one-off | |
| 392 | 396 | scripts. | |
| 393 | 397 | 4. Write the extension dir (manifest + source). | |
| 394 | - | 5. For Go, use `zot ext install --build=go <path>` or build it manually. | |
| 398 | + | 5. For Go, use `zut ext install --build=go <path>` or build it manually. | |
| 395 | 399 | For TS / Python, mark the script executable. | |
| 396 | - | 6. Suggest `zot --ext <path>` for testing without committing to an install. | |
| 397 | - | 7. When happy, `zot ext install <path>` (or `--build=go` for local Go source). | |
| 400 | + | 6. Suggest `zut --ext <path>` for testing without committing to an install. | |
| 401 | + | 7. When happy, `zut ext install <path>` (or `--build=go` for local Go source). | |
| 398 | 402 | ||
| 399 | 403 | Don't try to write a full SDK or framework on top of the protocol | |
| 400 | 404 | unless the user asked for one — the wire format is small enough | |
R packages/agent/skills/builtin/write-zot-themes/SKILL.md → packages/agent/skills/builtin/write-zut-themes/SKILL.md +29 -29 renamed, 83% similar
| @@ -1,17 +1,17 @@ | |||
| 1 | 1 | --- | |
| 2 | - | name: write-zot-themes | |
| 3 | - | description: Help the user create, install, or package zot themes, including theme-only extensions. | |
| 2 | + | name: write-zut-themes | |
| 3 | + | description: Help the user create, install, or package zut themes, including theme-only extensions. | |
| 4 | 4 | --- | |
| 5 | 5 | ||
| 6 | - | # Writing zot themes | |
| 6 | + | # Writing zut themes | |
| 7 | 7 | ||
| 8 | 8 | Use this skill when the user asks for help creating, editing, | |
| 9 | - | installing, debugging, or packaging a zot color theme. Read this skill | |
| 9 | + | installing, debugging, or packaging a zut color theme. Read this skill | |
| 10 | 10 | before generating a theme file or advising on theme extension layout. | |
| 11 | 11 | ||
| 12 | - | ## What a zot theme is | |
| 12 | + | ## What a zut theme is | |
| 13 | 13 | ||
| 14 | - | A zot theme is a JSON file that overrides any subset of zot's built-in | |
| 14 | + | A zut theme is a JSON file that overrides any subset of zut's built-in | |
| 15 | 15 | light/dark theme values. Theme files are intentionally permissive: | |
| 16 | 16 | nothing is required. A file may contain only colors, only spinner | |
| 17 | 17 | settings, only syntax colors, or only metadata. Missing values always | |
| @@ -27,19 +27,19 @@ unavailable. | |||
| 27 | 27 | User themes are discovered from: | |
| 28 | 28 | ||
| 29 | 29 | ```text | |
| 30 | - | $ZOT_HOME/themes/*.json | |
| 30 | + | $ZUT_HOME/themes/*.json | |
| 31 | 31 | ``` | |
| 32 | 32 | ||
| 33 | 33 | Extension themes are discovered in-place from loaded extension dirs: | |
| 34 | 34 | ||
| 35 | 35 | ```text | |
| 36 | - | $ZOT_HOME/extensions/<extension>/theme.json | |
| 37 | - | $ZOT_HOME/extensions/<extension>/themes/theme.json | |
| 38 | - | <project>/.zot/extensions/<extension>/theme.json | |
| 39 | - | <project>/.zot/extensions/<extension>/themes/theme.json | |
| 36 | + | $ZUT_HOME/extensions/<extension>/theme.json | |
| 37 | + | $ZUT_HOME/extensions/<extension>/themes/theme.json | |
| 38 | + | <project>/.zut/extensions/<extension>/theme.json | |
| 39 | + | <project>/.zut/extensions/<extension>/themes/theme.json | |
| 40 | 40 | ``` | |
| 41 | 41 | ||
| 42 | - | zot does **not** copy extension themes into `$ZOT_HOME/themes`; extension | |
| 42 | + | zut does **not** copy extension themes into `$ZUT_HOME/themes`; extension | |
| 43 | 43 | owned themes stay in the extension directory. The settings picker stores | |
| 44 | 44 | an absolute path for extension-owned themes and loads that file directly. | |
| 45 | 45 | ||
| @@ -47,12 +47,12 @@ an absolute path for extension-owned themes and loads that file directly. | |||
| 47 | 47 | ||
| 48 | 48 | ### Empty / metadata-only theme | |
| 49 | 49 | ||
| 50 | - | Valid; inherits all colors/spinner/syntax from zot defaults. | |
| 50 | + | Valid; inherits all colors/spinner/syntax from zut defaults. | |
| 51 | 51 | ||
| 52 | 52 | ```json | |
| 53 | 53 | { | |
| 54 | 54 | "name": "my-theme", | |
| 55 | - | "description": "Metadata only; all visuals inherit zot defaults." | |
| 55 | + | "description": "Metadata only; all visuals inherit zut defaults." | |
| 56 | 56 | } | |
| 57 | 57 | ``` | |
| 58 | 58 | ||
| @@ -95,7 +95,7 @@ Top-level spinner overrides are valid and apply to both modes. | |||
| 95 | 95 | ||
| 96 | 96 | ### Dark-only theme also works in light terminals | |
| 97 | 97 | ||
| 98 | - | If `colors.light` is missing, zot applies `colors.dark` overrides on | |
| 98 | + | If `colors.light` is missing, zut applies `colors.dark` overrides on | |
| 99 | 99 | top of the built-in light default when running in light mode. The | |
| 100 | 100 | inverse is also true: if dark is missing but light exists, light | |
| 101 | 101 | settings are used on dark defaults. | |
| @@ -103,7 +103,7 @@ settings are used on dark defaults. | |||
| 103 | 103 | ```json | |
| 104 | 104 | { | |
| 105 | 105 | "name": "custom-spinner", | |
| 106 | - | "description": "An alternative spinner for zot that only displays a single spinner text.", | |
| 106 | + | "description": "An alternative spinner for zut that only displays a single spinner text.", | |
| 107 | 107 | "colors": { | |
| 108 | 108 | "dark": { | |
| 109 | 109 | "spinner_frames": ["◢", "◣", "◤", "◥"], | |
| @@ -123,7 +123,7 @@ All fields below are optional. | |||
| 123 | 123 | "name": "my-theme", | |
| 124 | 124 | "description": "Shown in /settings → color theme.", | |
| 125 | 125 | "color_descriptions": { | |
| 126 | - | "accent": "Optional documentation for humans. zot ignores this object." | |
| 126 | + | "accent": "Optional documentation for humans. zut ignores this object." | |
| 127 | 127 | }, | |
| 128 | 128 | "colors": { | |
| 129 | 129 | "dark": { | |
| @@ -198,7 +198,7 @@ under `colors`, when they should apply to both modes: | |||
| 198 | 198 | - `user` — user role label color; mostly compatibility. | |
| 199 | 199 | - `user_bubble_bg` — background behind user message rows. | |
| 200 | 200 | - `user_bubble_fg` — foreground inside user message rows. | |
| 201 | - | - `assistant` — assistant/zot accent and spinner text. | |
| 201 | + | - `assistant` — assistant/zut accent and spinner text. | |
| 202 | 202 | - `tool` — tool names, success marks, diff additions. | |
| 203 | 203 | - `tool_out` — plain tool-output text. | |
| 204 | 204 | - `error` — errors, refused calls, diff deletions. | |
| @@ -230,7 +230,7 @@ Spinner settings can appear at top level, under `colors`, or under | |||
| 230 | 230 | ||
| 231 | 231 | - `spinner_frames` — list of frame strings. Use single-cell glyphs | |
| 232 | 232 | when possible so status-bar alignment stays clean. | |
| 233 | - | - `spinner_messages` — list of messages; zot chooses one per turn. | |
| 233 | + | - `spinner_messages` — list of messages; zut chooses one per turn. | |
| 234 | 234 | - `spinner_interval_ms` — frame interval in milliseconds; must be | |
| 235 | 235 | positive. Missing/invalid falls back to 80ms. | |
| 236 | 236 | ||
| @@ -291,26 +291,26 @@ Example: | |||
| 291 | 291 | ## Installing a user theme | |
| 292 | 292 | ||
| 293 | 293 | ```bash | |
| 294 | - | mkdir -p "$ZOT_HOME/themes" | |
| 295 | - | cp my-theme.json "$ZOT_HOME/themes/my-theme.json" | |
| 294 | + | mkdir -p "$ZUT_HOME/themes" | |
| 295 | + | cp my-theme.json "$ZUT_HOME/themes/my-theme.json" | |
| 296 | 296 | ``` | |
| 297 | 297 | ||
| 298 | - | Then open `/settings` and choose **color theme**. zot switches theme | |
| 299 | - | immediately and persists the selection in `$ZOT_HOME/config.json`. | |
| 298 | + | Then open `/settings` and choose **color theme**. zut switches theme | |
| 299 | + | immediately and persists the selection in `$ZUT_HOME/config.json`. | |
| 300 | 300 | ||
| 301 | - | If a selected theme file is deleted, zot resets the setting to the | |
| 301 | + | If a selected theme file is deleted, zut resets the setting to the | |
| 302 | 302 | built-in auto/default theme. | |
| 303 | 303 | ||
| 304 | 304 | ## Theme-only extensions | |
| 305 | 305 | ||
| 306 | - | A zot extension can exist only to ship a theme. No slash command, | |
| 306 | + | A zut extension can exist only to ship a theme. No slash command, | |
| 307 | 307 | subprocess, or executable is required when the extension contains a | |
| 308 | 308 | valid theme file. | |
| 309 | 309 | ||
| 310 | 310 | Layout: | |
| 311 | 311 | ||
| 312 | 312 | ```text | |
| 313 | - | $ZOT_HOME/extensions/my-theme-extension/ | |
| 313 | + | $ZUT_HOME/extensions/my-theme-extension/ | |
| 314 | 314 | ├── extension.json | |
| 315 | 315 | └── theme.json | |
| 316 | 316 | ``` | |
| @@ -318,7 +318,7 @@ $ZOT_HOME/extensions/my-theme-extension/ | |||
| 318 | 318 | or: | |
| 319 | 319 | ||
| 320 | 320 | ```text | |
| 321 | - | $ZOT_HOME/extensions/my-theme-extension/ | |
| 321 | + | $ZUT_HOME/extensions/my-theme-extension/ | |
| 322 | 322 | ├── extension.json | |
| 323 | 323 | └── themes/ | |
| 324 | 324 | └── theme.json | |
| @@ -330,13 +330,13 @@ $ZOT_HOME/extensions/my-theme-extension/ | |||
| 330 | 330 | { | |
| 331 | 331 | "name": "my-theme-extension", | |
| 332 | 332 | "version": "1.0.0", | |
| 333 | - | "description": "Ships a zot color theme", | |
| 333 | + | "description": "Ships a zut color theme", | |
| 334 | 334 | "enabled": true | |
| 335 | 335 | } | |
| 336 | 336 | ``` | |
| 337 | 337 | ||
| 338 | 338 | No `exec` is needed when `theme.json` or `themes/theme.json` exists. | |
| 339 | - | If `exec` is present, zot treats it as a normal extension too. | |
| 339 | + | If `exec` is present, zut treats it as a normal extension too. | |
| 340 | 340 | ||
| 341 | 341 | In `/settings → color theme`, extension themes show source info in | |
| 342 | 342 | the description, e.g. `from extension my-theme-extension — ...`. | |
M packages/agent/skills/skills.go +21 -21
| @@ -1,8 +1,8 @@ | |||
| 1 | - | // Package skills implements zot's reusable-instruction system. | |
| 1 | + | // Package skills implements zut's reusable-instruction system. | |
| 2 | 2 | // | |
| 3 | 3 | // A skill is a per-folder SKILL.md file with a YAML frontmatter | |
| 4 | 4 | // header. Skills live in well-known directories under the project or | |
| 5 | - | // the user home; zot discovers them at startup, lists their names + | |
| 5 | + | // the user home; zut discovers them at startup, lists their names + | |
| 6 | 6 | // one-line descriptions in the system prompt, and exposes a built-in | |
| 7 | 7 | // "skill" tool the model uses to pull the full body on demand. | |
| 8 | 8 | // | |
| @@ -12,15 +12,15 @@ | |||
| 12 | 12 | // | |
| 13 | 13 | // Discovery layout (priority order — first match wins per name): | |
| 14 | 14 | // | |
| 15 | - | // ./.zot/skills/<name>/SKILL.md — project (native) | |
| 16 | - | // $ZOT_HOME/skills/<name>/SKILL.md — global (native) | |
| 15 | + | // ./.zut/skills/<name>/SKILL.md — project (native) | |
| 16 | + | // $ZUT_HOME/skills/<name>/SKILL.md — global (native) | |
| 17 | 17 | // ./.claude/skills/<name>/SKILL.md — project (claude-compat) | |
| 18 | 18 | // ~/.claude/skills/<name>/SKILL.md — global (claude-compat) | |
| 19 | 19 | // ./.agents/skills/<name>/SKILL.md — project (agent-compat) | |
| 20 | 20 | // ~/.agents/skills/<name>/SKILL.md — global (agent-compat) | |
| 21 | 21 | // | |
| 22 | 22 | // The compat paths are deliberate: a SKILL.md written for any of | |
| 23 | - | // the related ecosystems works in zot unchanged. User skill roots are | |
| 23 | + | // the related ecosystems works in zut unchanged. User skill roots are | |
| 24 | 24 | // walked recursively, and nested files can also be addressed by their | |
| 25 | 25 | // slash-separated path relative to the root (for example, | |
| 26 | 26 | // systems-backend/subskills/golang-patterns). | |
| @@ -63,7 +63,7 @@ type Skill struct { | |||
| 63 | 63 | // Shown in the /skills picker. | |
| 64 | 64 | Source string | |
| 65 | 65 | ||
| 66 | - | // Builtin marks skills that ship inside the zot binary. They are | |
| 66 | + | // Builtin marks skills that ship inside the zut binary. They are | |
| 67 | 67 | // fully active for the model (system-prompt manifest + skill | |
| 68 | 68 | // tool) but hidden from user-facing surfaces like the /skills | |
| 69 | 69 | // picker so users only see skills they actually installed or | |
| @@ -121,18 +121,18 @@ func VisibleSkills(in []*Skill) []*Skill { | |||
| 121 | 121 | // | |
| 122 | 122 | // Errors per skill are returned alongside the partial result so a | |
| 123 | 123 | // single broken file doesn't suppress the rest. | |
| 124 | - | func Discover(zotHome, cwd, userHome string, includeUser bool) ([]*Skill, []error) { | |
| 125 | - | return DiscoverWithBundled(zotHome, cwd, userHome, includeUser, nil) | |
| 124 | + | func Discover(zutHome, cwd, userHome string, includeUser bool) ([]*Skill, []error) { | |
| 125 | + | return DiscoverWithBundled(zutHome, cwd, userHome, includeUser, nil) | |
| 126 | 126 | } | |
| 127 | 127 | ||
| 128 | 128 | // DiscoverWithBundled merges normal user skills with extension-bundled skills | |
| 129 | 129 | // and built-ins. Precedence is user/project locations, then bundled skills in | |
| 130 | 130 | // declaration order, then built-ins. The first matching name wins. | |
| 131 | - | func DiscoverWithBundled(zotHome, cwd, userHome string, includeUser bool, bundled []BundledSkillDir) ([]*Skill, []error) { | |
| 131 | + | func DiscoverWithBundled(zutHome, cwd, userHome string, includeUser bool, bundled []BundledSkillDir) ([]*Skill, []error) { | |
| 132 | 132 | var errs []error | |
| 133 | 133 | seen := map[string]*Skill{} | |
| 134 | 134 | if includeUser { | |
| 135 | - | errs = append(errs, scanUserSkills(zotHome, cwd, userHome, seen)...) | |
| 135 | + | errs = append(errs, scanUserSkills(zutHome, cwd, userHome, seen)...) | |
| 136 | 136 | } | |
| 137 | 137 | for _, dir := range bundled { | |
| 138 | 138 | errs = append(errs, scanBundledSkills(dir, seen)...) | |
| @@ -250,9 +250,9 @@ func scanBundledSkills(dir BundledSkillDir, seen map[string]*Skill) []error { | |||
| 250 | 250 | // populates `seen` with first-match-wins per name or alias. Split out | |
| 251 | 251 | // so Discover's includeUser=false path doesn't have to skip over a | |
| 252 | 252 | // giant block. | |
| 253 | - | func scanUserSkills(zotHome, cwd, userHome string, seen map[string]*Skill) []error { | |
| 253 | + | func scanUserSkills(zutHome, cwd, userHome string, seen map[string]*Skill) []error { | |
| 254 | 254 | var errs []error | |
| 255 | - | for _, loc := range searchDirs(zotHome, cwd, userHome) { | |
| 255 | + | for _, loc := range searchDirs(zutHome, cwd, userHome) { | |
| 256 | 256 | _ = filepath.WalkDir(loc.dir, func(path string, entry fs.DirEntry, walkErr error) error { | |
| 257 | 257 | if walkErr != nil { | |
| 258 | 258 | if !os.IsNotExist(walkErr) { | |
| @@ -362,7 +362,7 @@ func uniqueSkills(seen map[string]*Skill) []*Skill { | |||
| 362 | 362 | // The format is deliberately compact: name, one-line description, | |
| 363 | 363 | // and a source pointer telling the model where the full body | |
| 364 | 364 | // lives. Built-in skills show "builtin" since their markdown is | |
| 365 | - | // embedded in the zot binary and not on the filesystem; user | |
| 365 | + | // embedded in the zut binary and not on the filesystem; user | |
| 366 | 366 | // skills show their SKILL.md path (shortened with ~ for HOME). | |
| 367 | 367 | // | |
| 368 | 368 | // Loading still goes through the `skill` tool with just the name. | |
| @@ -391,7 +391,7 @@ func SystemPromptAddendum(skills []*Skill) string { | |||
| 391 | 391 | ||
| 392 | 392 | // skillSourcePointer returns a short tag describing where a skill | |
| 393 | 393 | // originates. Built-ins are tagged "builtin" because their markdown | |
| 394 | - | // is embedded in the zot binary and not reachable through the | |
| 394 | + | // is embedded in the zut binary and not reachable through the | |
| 395 | 395 | // filesystem. User skills are tagged with their SKILL.md path, | |
| 396 | 396 | // collapsed to use ~ for the user home when possible. | |
| 397 | 397 | func skillSourcePointer(s *Skill, home string) string { | |
| @@ -438,7 +438,7 @@ type location struct { | |||
| 438 | 438 | label string | |
| 439 | 439 | } | |
| 440 | 440 | ||
| 441 | - | func searchDirs(zotHome, cwd, userHome string) []location { | |
| 441 | + | func searchDirs(zutHome, cwd, userHome string) []location { | |
| 442 | 442 | var out []location | |
| 443 | 443 | add := func(dir, label string) { | |
| 444 | 444 | if dir == "" { | |
| @@ -446,16 +446,16 @@ func searchDirs(zotHome, cwd, userHome string) []location { | |||
| 446 | 446 | } | |
| 447 | 447 | out = append(out, location{dir: dir, label: label}) | |
| 448 | 448 | } | |
| 449 | - | if extra := os.Getenv("ZOT_AGENT_SKILLS"); extra != "" { | |
| 449 | + | if extra := os.Getenv("ZUT_AGENT_SKILLS"); extra != "" { | |
| 450 | 450 | for _, dir := range filepath.SplitList(extra) { | |
| 451 | 451 | add(dir, "agent") | |
| 452 | 452 | } | |
| 453 | 453 | } | |
| 454 | 454 | if cwd != "" { | |
| 455 | - | add(filepath.Join(cwd, ".zot", "skills"), "project") | |
| 455 | + | add(filepath.Join(cwd, ".zut", "skills"), "project") | |
| 456 | 456 | } | |
| 457 | - | if zotHome != "" { | |
| 458 | - | add(filepath.Join(zotHome, "skills"), "global") | |
| 457 | + | if zutHome != "" { | |
| 458 | + | add(filepath.Join(zutHome, "skills"), "global") | |
| 459 | 459 | } | |
| 460 | 460 | if cwd != "" { | |
| 461 | 461 | add(filepath.Join(cwd, ".claude", "skills"), "project (claude)") | |
| @@ -510,14 +510,14 @@ func splitFrontmatter(raw string) (string, string) { | |||
| 510 | 510 | return front, body | |
| 511 | 511 | } | |
| 512 | 512 | ||
| 513 | - | // parseFrontmatter handles the small subset of YAML zot recognizes: | |
| 513 | + | // parseFrontmatter handles the small subset of YAML zut recognizes: | |
| 514 | 514 | // - simple `key: value` lines | |
| 515 | 515 | // - `key: [a, b, c]` flow-style lists | |
| 516 | 516 | // - `key:` followed by indented `- item` block lists | |
| 517 | 517 | // - nested `key:` followed by indented `subkey: [...]` for permissions | |
| 518 | 518 | // | |
| 519 | 519 | // Anything more elaborate is ignored. We deliberately avoid a yaml | |
| 520 | - | // dependency to keep zot's binary lean. | |
| 520 | + | // dependency to keep zut's binary lean. | |
| 521 | 521 | func parseFrontmatter(front string, s *Skill) { | |
| 522 | 522 | lines := strings.Split(front, "\n") | |
| 523 | 523 | i := 0 | |
M packages/agent/skills/skills_test.go +10 -10
| @@ -50,9 +50,9 @@ permissions: | |||
| 50 | 50 | } | |
| 51 | 51 | ||
| 52 | 52 | func TestDiscoverProjectAndGlobalPriorityAndDedup(t *testing.T) { | |
| 53 | - | t.Setenv("ZOT_AGENT_SKILLS", "") | |
| 53 | + | t.Setenv("ZUT_AGENT_SKILLS", "") | |
| 54 | 54 | tmp := t.TempDir() | |
| 55 | - | zotHome := filepath.Join(tmp, "home") | |
| 55 | + | zutHome := filepath.Join(tmp, "home") | |
| 56 | 56 | cwd := filepath.Join(tmp, "proj") | |
| 57 | 57 | ||
| 58 | 58 | mk := func(dir, name, desc string) { | |
| @@ -63,17 +63,17 @@ func TestDiscoverProjectAndGlobalPriorityAndDedup(t *testing.T) { | |||
| 63 | 63 | } | |
| 64 | 64 | ||
| 65 | 65 | // Same skill name in BOTH project and global; project should win. | |
| 66 | - | mk(filepath.Join(cwd, ".zot", "skills"), "shared", "project version") | |
| 67 | - | mk(filepath.Join(zotHome, "skills"), "shared", "global version") | |
| 66 | + | mk(filepath.Join(cwd, ".zut", "skills"), "shared", "project version") | |
| 67 | + | mk(filepath.Join(zutHome, "skills"), "shared", "global version") | |
| 68 | 68 | // Unique skill in global only. | |
| 69 | - | mk(filepath.Join(zotHome, "skills"), "global-only", "from global") | |
| 69 | + | mk(filepath.Join(zutHome, "skills"), "global-only", "from global") | |
| 70 | 70 | ||
| 71 | - | skills, errs := Discover(zotHome, cwd, "", true /* includeUser */) | |
| 71 | + | skills, errs := Discover(zutHome, cwd, "", true /* includeUser */) | |
| 72 | 72 | if len(errs) > 0 { | |
| 73 | 73 | t.Fatalf("errs: %v", errs) | |
| 74 | 74 | } | |
| 75 | 75 | // Expect the two user skills + every built-in shipped with the | |
| 76 | - | // binary (currently the write-zot-extension authoring guide). | |
| 76 | + | // binary (currently the write-zut-extension authoring guide). | |
| 77 | 77 | builtins := loadBuiltins() | |
| 78 | 78 | want := 2 + len(builtins) | |
| 79 | 79 | if len(skills) != want { | |
| @@ -95,10 +95,10 @@ func TestDiscoverProjectAndGlobalPriorityAndDedup(t *testing.T) { | |||
| 95 | 95 | } | |
| 96 | 96 | ||
| 97 | 97 | func TestDiscoverWithBundledSkillsPrecedence(t *testing.T) { | |
| 98 | - | t.Setenv("ZOT_AGENT_SKILLS", "") | |
| 98 | + | t.Setenv("ZUT_AGENT_SKILLS", "") | |
| 99 | 99 | tmp := t.TempDir() | |
| 100 | 100 | cwd := filepath.Join(tmp, "project") | |
| 101 | - | userDir := filepath.Join(cwd, ".zot", "skills") | |
| 101 | + | userDir := filepath.Join(cwd, ".zut", "skills") | |
| 102 | 102 | bundleA := filepath.Join(tmp, "ext-a", "skills") | |
| 103 | 103 | bundleB := filepath.Join(tmp, "ext-b", "skills") | |
| 104 | 104 | write := func(root, name, description string) { | |
| @@ -165,7 +165,7 @@ func TestVisibleSkillsHidesBuiltins(t *testing.T) { | |||
| 165 | 165 | } | |
| 166 | 166 | ||
| 167 | 167 | func TestDiscoverRecursesNestedAgentSkillsAndSupportsPathAlias(t *testing.T) { | |
| 168 | - | t.Setenv("ZOT_AGENT_SKILLS", "") | |
| 168 | + | t.Setenv("ZUT_AGENT_SKILLS", "") | |
| 169 | 169 | tmp := t.TempDir() | |
| 170 | 170 | home := filepath.Join(tmp, "home") | |
| 171 | 171 | cwd := filepath.Join(tmp, "project") | |
M packages/agent/skills/tool.go +3 -3
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "fmt" | |
| 7 | 7 | "sync" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // Tool implements core.Tool, exposing a `skill` tool the LLM can call | |
| @@ -84,7 +84,7 @@ func (t *Tool) Execute(ctx context.Context, args json.RawMessage, progress func( | |||
| 84 | 84 | if s == nil { | |
| 85 | 85 | return core.ToolResult{ | |
| 86 | 86 | IsError: true, | |
| 87 | - | Content: []provider.Content{provider.TextBlock{Text: fmt.Sprintf("skill: no skill named %q (run /skills in zot to see what's available)", in.Name)}}, | |
| 87 | + | Content: []provider.Content{provider.TextBlock{Text: fmt.Sprintf("skill: no skill named %q (run /skills in zut to see what's available)", in.Name)}}, | |
| 88 | 88 | }, nil | |
| 89 | 89 | } | |
| 90 | 90 | ||
M packages/agent/skills/tool_test.go +1 -1
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "testing" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | func TestToolLoadsSkillByPathAlias(t *testing.T) { | |
M packages/agent/subagent_worker.go +15 -15
| @@ -13,14 +13,14 @@ import ( | |||
| 13 | 13 | "time" | |
| 14 | 14 | "unicode/utf8" | |
| 15 | 15 | ||
| 16 | - | "github.com/patriceckhart/zot/packages/agent/modes" | |
| 17 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 18 | - | "github.com/patriceckhart/zot/packages/core" | |
| 19 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 16 | + | "github.com/bnema/zut/packages/agent/modes" | |
| 17 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 18 | + | "github.com/bnema/zut/packages/core" | |
| 19 | + | "github.com/bnema/zut/packages/provider" | |
| 20 | 20 | ) | |
| 21 | 21 | ||
| 22 | 22 | // runSubagentWorkerMode is the daemon-mode entry point used by every | |
| 23 | - | // subagent-spawned zot subprocess. It's intentionally close in shape to | |
| 23 | + | // subagent-spawned zut subprocess. It's intentionally close in shape to | |
| 24 | 24 | // runJSONMode but with two key differences: | |
| 25 | 25 | // | |
| 26 | 26 | // - Lifetime: the process stays alive across many user turns. The | |
| @@ -28,7 +28,7 @@ import ( | |||
| 28 | 28 | // turns arrive through the inbox unix socket at args.SubagentWorker. | |
| 29 | 29 | // | |
| 30 | 30 | // - Output: every emitted JSON line is also mirrored verbatim into | |
| 31 | - | // events.jsonl (see ZOT_SUBAGENT_EVENT_LOG) so a separate zot | |
| 31 | + | // events.jsonl (see ZUT_SUBAGENT_EVENT_LOG) so a separate zut | |
| 32 | 32 | // process can /subagents open this agent and replay its full history | |
| 33 | 33 | // even after the parent that spawned us is long gone. | |
| 34 | 34 | // | |
| @@ -40,7 +40,7 @@ func runSubagentWorkerMode(ctx context.Context, args Args, version string) error | |||
| 40 | 40 | if args.SubagentWorker == "" { | |
| 41 | 41 | return fmt.Errorf("--subagent-worker requires a socket path") | |
| 42 | 42 | } | |
| 43 | - | if os.Getenv("ZOT_SUBAGENT_CREDENTIAL_STDIN") == "1" { | |
| 43 | + | if os.Getenv("ZUT_SUBAGENT_CREDENTIAL_STDIN") == "1" { | |
| 44 | 44 | var inherited subagents.Credential | |
| 45 | 45 | dec := json.NewDecoder(io.LimitReader(os.Stdin, 1<<20)) | |
| 46 | 46 | if err := dec.Decode(&inherited); err != nil { | |
| @@ -99,11 +99,11 @@ func runSubagentWorkerMode(ctx context.Context, args Args, version string) error | |||
| 99 | 99 | ||
| 100 | 100 | // Event log is owned by the supervisor's runner via stdout, but | |
| 101 | 101 | // the daemon also writes a redundant copy here when the runner's | |
| 102 | - | // pipe is closed (e.g. parent zot exited but the agent is still | |
| 102 | + | // pipe is closed (e.g. parent zut exited but the agent is still | |
| 103 | 103 | // running headless). The env var is set by the runner; if it's | |
| 104 | 104 | // empty we silently skip the second mirror. | |
| 105 | 105 | var logMirror *subagents.EventLog | |
| 106 | - | if path := os.Getenv("ZOT_SUBAGENT_EVENT_LOG"); path != "" { | |
| 106 | + | if path := os.Getenv("ZUT_SUBAGENT_EVENT_LOG"); path != "" { | |
| 107 | 107 | logMirror, _ = subagents.OpenEventLog(path) | |
| 108 | 108 | } | |
| 109 | 109 | if logMirror != nil { | |
| @@ -111,7 +111,7 @@ func runSubagentWorkerMode(ctx context.Context, args Args, version string) error | |||
| 111 | 111 | } | |
| 112 | 112 | ||
| 113 | 113 | em := newSubagentEmitter(os.Stdout, logMirror) | |
| 114 | - | em.setProtocolIdentity(os.Getenv("ZOT_SUBAGENT_AGENT_ID")) | |
| 114 | + | em.setProtocolIdentity(os.Getenv("ZUT_SUBAGENT_AGENT_ID")) | |
| 115 | 115 | em.emit("agent.ready", map[string]any{ | |
| 116 | 116 | "version": version, | |
| 117 | 117 | "cwd": r.CWD, | |
| @@ -125,7 +125,7 @@ func runSubagentWorkerMode(ctx context.Context, args Args, version string) error | |||
| 125 | 125 | defer close(heartbeatDone) | |
| 126 | 126 | go func() { | |
| 127 | 127 | interval := 10 * time.Second | |
| 128 | - | if raw := os.Getenv("ZOT_SUBAGENT_HEARTBEAT_INTERVAL"); raw != "" { | |
| 128 | + | if raw := os.Getenv("ZUT_SUBAGENT_HEARTBEAT_INTERVAL"); raw != "" { | |
| 129 | 129 | if parsed, parseErr := time.ParseDuration(raw); parseErr == nil && parsed > 0 { | |
| 130 | 130 | interval = parsed | |
| 131 | 131 | } | |
| @@ -148,7 +148,7 @@ func runSubagentWorkerMode(ctx context.Context, args Args, version string) error | |||
| 148 | 148 | mu sync.Mutex | |
| 149 | 149 | cancelFn context.CancelFunc | |
| 150 | 150 | busyTurn bool | |
| 151 | - | turnNo = initialTurnNumber(os.Getenv("ZOT_SUBAGENT_EVENT_LOG")) | |
| 151 | + | turnNo = initialTurnNumber(os.Getenv("ZUT_SUBAGENT_EVENT_LOG")) | |
| 152 | 152 | turnWG sync.WaitGroup | |
| 153 | 153 | closing bool | |
| 154 | 154 | shutdown = make(chan struct{}) | |
| @@ -390,7 +390,7 @@ func (e *subagentEmitter) emit(typ string, data map[string]any) { | |||
| 390 | 390 | ||
| 391 | 391 | if !e.orphan && err == nil { | |
| 392 | 392 | if _, werr := e.w.Write(line); werr != nil { | |
| 393 | - | // Supervisor's stdout pipe is gone (parent zot exited but we | |
| 393 | + | // Supervisor's stdout pipe is gone (parent zut exited but we | |
| 394 | 394 | // kept running). Switch to mirror-only mode and preserve this | |
| 395 | 395 | // event in the durable log. | |
| 396 | 396 | e.orphan = true | |
| @@ -451,10 +451,10 @@ func boundedResultSummary(text string) string { | |||
| 451 | 451 | ||
| 452 | 452 | func workerOutputLimits() (maxBytes, maxLines int) { | |
| 453 | 453 | maxBytes, maxLines = 500_000, 5_000 | |
| 454 | - | if value, err := strconv.Atoi(strings.TrimSpace(os.Getenv("ZOT_SUBAGENT_MAX_OUTPUT_BYTES"))); err == nil && value > 0 { | |
| 454 | + | if value, err := strconv.Atoi(strings.TrimSpace(os.Getenv("ZUT_SUBAGENT_MAX_OUTPUT_BYTES"))); err == nil && value > 0 { | |
| 455 | 455 | maxBytes = value | |
| 456 | 456 | } | |
| 457 | - | if value, err := strconv.Atoi(strings.TrimSpace(os.Getenv("ZOT_SUBAGENT_MAX_OUTPUT_LINES"))); err == nil && value > 0 { | |
| 457 | + | if value, err := strconv.Atoi(strings.TrimSpace(os.Getenv("ZUT_SUBAGENT_MAX_OUTPUT_LINES"))); err == nil && value > 0 { | |
| 458 | 458 | maxLines = value | |
| 459 | 459 | } | |
| 460 | 460 | return maxBytes, maxLines | |
M packages/agent/subagent_worker_test.go +6 -6
| @@ -9,21 +9,21 @@ import ( | |||
| 9 | 9 | "strings" | |
| 10 | 10 | "testing" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 12 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | // TestWorkerOutputLimitsUseSupervisorPolicy verifies that the child reads | |
| 16 | 16 | // the effective output caps propagated by the supervisor and retains safe | |
| 17 | 17 | // defaults for standalone workers. | |
| 18 | 18 | func TestWorkerOutputLimitsUseSupervisorPolicy(t *testing.T) { | |
| 19 | - | t.Setenv("ZOT_SUBAGENT_MAX_OUTPUT_BYTES", "123") | |
| 20 | - | t.Setenv("ZOT_SUBAGENT_MAX_OUTPUT_LINES", "7") | |
| 19 | + | t.Setenv("ZUT_SUBAGENT_MAX_OUTPUT_BYTES", "123") | |
| 20 | + | t.Setenv("ZUT_SUBAGENT_MAX_OUTPUT_LINES", "7") | |
| 21 | 21 | bytesLimit, linesLimit := workerOutputLimits() | |
| 22 | 22 | if bytesLimit != 123 || linesLimit != 7 { | |
| 23 | 23 | t.Fatalf("worker output limits = (%d, %d), want (123, 7)", bytesLimit, linesLimit) | |
| 24 | 24 | } | |
| 25 | - | t.Setenv("ZOT_SUBAGENT_MAX_OUTPUT_BYTES", "invalid") | |
| 26 | - | t.Setenv("ZOT_SUBAGENT_MAX_OUTPUT_LINES", "0") | |
| 25 | + | t.Setenv("ZUT_SUBAGENT_MAX_OUTPUT_BYTES", "invalid") | |
| 26 | + | t.Setenv("ZUT_SUBAGENT_MAX_OUTPUT_LINES", "0") | |
| 27 | 27 | bytesLimit, linesLimit = workerOutputLimits() | |
| 28 | 28 | if bytesLimit != 500_000 || linesLimit != 5_000 { | |
| 29 | 29 | t.Fatalf("invalid worker output limits = (%d, %d), want defaults", bytesLimit, linesLimit) | |
| @@ -35,7 +35,7 @@ func TestWorkerOutputLimitsUseSupervisorPolicy(t *testing.T) { | |||
| 35 | 35 | // | |
| 36 | 36 | // Symptom: events.jsonl held two copies of every event because the | |
| 37 | 37 | // child mirrored each event to disk AND the supervisor parsed the | |
| 38 | - | // child's stdout and appended each event to disk too. On next zot | |
| 38 | + | // child's stdout and appended each event to disk too. On next zut | |
| 39 | 39 | // launch the replay produced two transcript lines per real one. | |
| 40 | 40 | // | |
| 41 | 41 | // Fix invariant: while stdout writes succeed (i.e. the supervisor is | |
M packages/agent/subagents/agent.go +3 -3
| @@ -47,7 +47,7 @@ type Agent struct { | |||
| 47 | 47 | // the effective provider connection settings from the supervisor. | |
| 48 | 48 | // Empty values inherit whatever the child resolves on its own from | |
| 49 | 49 | // config / env / flags. All four settings are persisted in meta.json | |
| 50 | - | // so Resume keeps using the same provider configuration across zot | |
| 50 | + | // so Resume keeps using the same provider configuration across zut | |
| 51 | 51 | // restarts. | |
| 52 | 52 | Model string | |
| 53 | 53 | Provider string | |
| @@ -58,7 +58,7 @@ type Agent struct { | |||
| 58 | 58 | Subagent string | |
| 59 | 59 | ||
| 60 | 60 | // SessionID, when non-empty, scopes the agent to a particular | |
| 61 | - | // host zot session: the dashboard only surfaces agents whose | |
| 61 | + | // host zut session: the dashboard only surfaces agents whose | |
| 62 | 62 | // SessionID matches the active session. Empty means "unscoped" and | |
| 63 | 63 | // applies to agents spawned without a session context such as in tests. Set at | |
| 64 | 64 | // Spawn time from Supervisor.activeSession and persisted in | |
| @@ -93,7 +93,7 @@ type Agent struct { | |||
| 93 | 93 | // EventLogPath is the durable JSONL event log for this agent. | |
| 94 | 94 | // The runner appends every well-formed event from the child | |
| 95 | 95 | // (plus lifecycle events of its own) here. /subagents open in any | |
| 96 | - | // zot process reads from this file to replay the full history. | |
| 96 | + | // zut process reads from this file to replay the full history. | |
| 97 | 97 | EventLogPath string | |
| 98 | 98 | ||
| 99 | 99 | // SessionPath is the child's persistent session file. Surfaced | |
M packages/agent/subagents/event_test.go +1 -1
| @@ -192,7 +192,7 @@ func TestReadEventLogIgnoresGarbage(t *testing.T) { | |||
| 192 | 192 | // supervisor parsed child stdout and called log.Append(ev), AND the | |
| 193 | 193 | // child's mirror appended the same event. Two writes per event | |
| 194 | 194 | // landed in the file in quick succession with near-identical | |
| 195 | - | // timestamps, and the next zot launch replayed everything twice — | |
| 195 | + | // timestamps, and the next zut launch replayed everything twice — | |
| 196 | 196 | // the user saw every transcript line duplicated. | |
| 197 | 197 | // | |
| 198 | 198 | // The write-time fix makes the child's mirror dormant when the | |
M packages/agent/subagents/fast_mode_test.go +5 -5
| @@ -37,7 +37,7 @@ func TestSupervisorFastModePropagatesToChild(t *testing.T) { | |||
| 37 | 37 | if !a.FastMode { | |
| 38 | 38 | t.Fatal("spawned agent FastMode = false, want true") | |
| 39 | 39 | } | |
| 40 | - | args := defaultChildArgs("/zot", a, "/session", "/inbox") | |
| 40 | + | args := defaultChildArgs("/zut", a, "/session", "/inbox") | |
| 41 | 41 | if !containsArg(args, "--fast-mode") { | |
| 42 | 42 | t.Fatalf("child args = %v, want --fast-mode", args) | |
| 43 | 43 | } | |
| @@ -91,7 +91,7 @@ func TestSpawnRequestFastModeIsBoundByHostSetting(t *testing.T) { | |||
| 91 | 91 | if a.FastMode != tc.want { | |
| 92 | 92 | t.Fatalf("FastMode = %v, want %v", a.FastMode, tc.want) | |
| 93 | 93 | } | |
| 94 | - | args := defaultChildArgs("/zot", a, "/session", "/inbox") | |
| 94 | + | args := defaultChildArgs("/zut", a, "/session", "/inbox") | |
| 95 | 95 | wantFlag := "--no-fast-mode" | |
| 96 | 96 | if tc.want { | |
| 97 | 97 | wantFlag = "--fast-mode" | |
| @@ -142,7 +142,7 @@ func TestSupervisorReloadPreservesExplicitFastModeOff(t *testing.T) { | |||
| 142 | 142 | } | |
| 143 | 143 | ||
| 144 | 144 | func TestSubagentWorkerArgsOmitFastModeWhenUnset(t *testing.T) { | |
| 145 | - | args := subagentWorkerArgs(subagentWorkerArgsOpts{Exe: "/zot", Dir: "/wt", SessionPath: "/s", InboxPath: "/i"}) | |
| 145 | + | args := subagentWorkerArgs(subagentWorkerArgsOpts{Exe: "/zut", Dir: "/wt", SessionPath: "/s", InboxPath: "/i"}) | |
| 146 | 146 | if strings.Contains(strings.Join(args, " "), "--fast-mode") || strings.Contains(strings.Join(args, " "), "--no-fast-mode") { | |
| 147 | 147 | t.Fatalf("child args = %v, want fast mode omitted when unset", args) | |
| 148 | 148 | } | |
| @@ -150,7 +150,7 @@ func TestSubagentWorkerArgsOmitFastModeWhenUnset(t *testing.T) { | |||
| 150 | 150 | ||
| 151 | 151 | func TestSubagentWorkerArgsExplicitlyDisablesFastMode(t *testing.T) { | |
| 152 | 152 | args := subagentWorkerArgs(subagentWorkerArgsOpts{ | |
| 153 | - | Exe: "/zot", | |
| 153 | + | Exe: "/zut", | |
| 154 | 154 | Dir: "/wt", | |
| 155 | 155 | SessionPath: "/s", | |
| 156 | 156 | InboxPath: "/i", | |
| @@ -165,7 +165,7 @@ func TestSubagentWorkerArgsExplicitlyDisablesFastMode(t *testing.T) { | |||
| 165 | 165 | } | |
| 166 | 166 | ||
| 167 | 167 | func TestDefaultChildArgsExplicitlyDisablesFastMode(t *testing.T) { | |
| 168 | - | args := defaultChildArgs("/zot", &Agent{Dir: "/wt"}, "/s", "/i") | |
| 168 | + | args := defaultChildArgs("/zut", &Agent{Dir: "/wt"}, "/s", "/i") | |
| 169 | 169 | if !containsArg(args, "--no-fast-mode") { | |
| 170 | 170 | t.Fatalf("child args = %v, want --no-fast-mode", args) | |
| 171 | 171 | } | |
M packages/agent/subagents/inbox.go +2 -2
| @@ -142,7 +142,7 @@ func isNoListenerErr(err error) bool { | |||
| 142 | 142 | // distinguish a live worker from a stale path. Callers use this probe before | |
| 143 | 143 | // resume/remove operations to avoid racing a detached worker's session and | |
| 144 | 144 | // workspace ownership. | |
| 145 | - | const inboxProbeLine = "__zot_subagent_probe__" | |
| 145 | + | const inboxProbeLine = "__zut_subagent_probe__" | |
| 146 | 146 | ||
| 147 | 147 | func inboxLive(path string) bool { | |
| 148 | 148 | if path == "" { | |
| @@ -169,7 +169,7 @@ type Listener struct { | |||
| 169 | 169 | path string | |
| 170 | 170 | ln net.Listener | |
| 171 | 171 | // active is the most recent accepted connection. The protocol | |
| 172 | - | // only expects one supervisor (the parent zot), so newer | |
| 172 | + | // only expects one supervisor (the parent zut), so newer | |
| 173 | 173 | // connections preempt older ones — the previous parent | |
| 174 | 174 | // presumably crashed. | |
| 175 | 175 | mu sync.Mutex | |
M packages/agent/subagents/inbox_test.go +2 -2
| @@ -22,7 +22,7 @@ func shortSocketDir(t *testing.T) string { | |||
| 22 | 22 | if runtime.GOOS == "windows" { | |
| 23 | 23 | t.Skip("subagent inbox transport uses Unix-domain sockets; Windows support will use a named-pipe backend") | |
| 24 | 24 | } | |
| 25 | - | dir, err := os.MkdirTemp("/tmp", "zot-in-") | |
| 25 | + | dir, err := os.MkdirTemp("/tmp", "zut-in-") | |
| 26 | 26 | if err != nil { | |
| 27 | 27 | t.Fatalf("mkdir: %v", err) | |
| 28 | 28 | } | |
| @@ -119,7 +119,7 @@ func TestIsNoListenerErrMatchesECONNREFUSED(t *testing.T) { | |||
| 119 | 119 | ||
| 120 | 120 | // TestInboxNewSupervisorPreemptsOld documents that only one | |
| 121 | 121 | // supervisor connection at a time is honoured. The subagent design | |
| 122 | - | // assumes a single parent zot owns each agent; if a second | |
| 122 | + | // assumes a single parent zut owns each agent; if a second | |
| 123 | 123 | // parent dials, the listener boots the first one. The first | |
| 124 | 124 | // supervisor's already-delivered messages still land in the | |
| 125 | 125 | // channel — what we're guarding against is silent dual ownership, | |
M packages/agent/subagents/persist.go +5 -5
| @@ -5,7 +5,7 @@ package subagents | |||
| 5 | 5 | // Every Spawn writes a meta.json next to the agent's events.jsonl and | |
| 6 | 6 | // session.json. The file captures the immutable identity bits (id, | |
| 7 | 7 | // task, branch, dir) plus the paths the runner needs to resume the | |
| 8 | - | // agent later. On a fresh zot launch, Supervisor.Reload() walks | |
| 8 | + | // agent later. On a fresh zut launch, Supervisor.Reload() walks | |
| 9 | 9 | // <root>/agents/*/meta.json and re-registers every agent it finds in | |
| 10 | 10 | // StatusDetached so the user can see, view, resume, or remove them | |
| 11 | 11 | // from the dashboard. | |
| @@ -76,7 +76,7 @@ type agentMeta struct { | |||
| 76 | 76 | SessionPath string `json:"session_path"` | |
| 77 | 77 | ||
| 78 | 78 | // SessionID, when non-empty, scopes the agent to a particular | |
| 79 | - | // host zot session: the dashboard only shows agents whose | |
| 79 | + | // host zut session: the dashboard only shows agents whose | |
| 80 | 80 | // SessionID matches the active session. Older meta.json files | |
| 81 | 81 | // (and agents spawned outside of any session, e.g. by tests or | |
| 82 | 82 | // scripted callers that didn't call SetActiveSession) have an | |
| @@ -218,7 +218,7 @@ func validRuntimeInboxPath(path, id string) bool { | |||
| 218 | 218 | return false | |
| 219 | 219 | } | |
| 220 | 220 | dir := filepath.Dir(path) | |
| 221 | - | if !strings.HasPrefix(filepath.Base(dir), "zot-subagents-") { | |
| 221 | + | if !strings.HasPrefix(filepath.Base(dir), "zut-subagents-") { | |
| 222 | 222 | return false | |
| 223 | 223 | } | |
| 224 | 224 | bases := []string{os.TempDir(), "/tmp"} | |
| @@ -558,7 +558,7 @@ func replayEventsIntoAgent(a *Agent, evs []Event) { | |||
| 558 | 558 | // Resume re-attaches a Runner to a previously-spawned agent. Durable | |
| 559 | 559 | // session and event files are kept, while the transient inbox path is | |
| 560 | 560 | // recalculated for the current runtime environment. Use this to | |
| 561 | - | // continue a subagent session across zot restarts: | |
| 561 | + | // continue a subagent session across zut restarts: | |
| 562 | 562 | // | |
| 563 | 563 | // subagentMgr.Reload() | |
| 564 | 564 | // a, err := subagentMgr.Resume(ctx, "alpha-12345") | |
| @@ -627,7 +627,7 @@ func (f *Supervisor) resume(ctx context.Context, id string, resuming bool) (*Age | |||
| 627 | 627 | // Rebuild from the meta record so we don't carry stale runner | |
| 628 | 628 | // state from a previous incarnation. The inbox is transient and | |
| 629 | 629 | // may have been persisted under an incompatible filesystem by an | |
| 630 | - | // older zot version, so always select it again on resume. | |
| 630 | + | // older zut version, so always select it again on resume. | |
| 631 | 631 | inboxPath, err := inboxSocketPath(f.cfg.Root, existing.ID) | |
| 632 | 632 | if err != nil { | |
| 633 | 633 | return nil, fmt.Errorf("subagent inbox path: %w", err) | |
M packages/agent/subagents/persist_test.go +5 -5
| @@ -16,7 +16,7 @@ import ( | |||
| 16 | 16 | ||
| 17 | 17 | // TestSpawnWritesMetaJSON asserts the durability contract: every | |
| 18 | 18 | // successful Spawn leaves a meta.json on disk with the agent's | |
| 19 | - | // identity bits. Without this, Reload on the next zot launch can't | |
| 19 | + | // identity bits. Without this, Reload on the next zut launch can't | |
| 20 | 20 | // find the agent and the user loses access to the worktree. | |
| 21 | 21 | func TestSpawnWritesMetaJSON(t *testing.T) { | |
| 22 | 22 | root := t.TempDir() | |
| @@ -63,7 +63,7 @@ func TestSpawnWritesMetaJSON(t *testing.T) { | |||
| 63 | 63 | } | |
| 64 | 64 | } | |
| 65 | 65 | ||
| 66 | - | // TestReloadRebuildsDetachedAgents simulates a zot restart by spawning | |
| 66 | + | // TestReloadRebuildsDetachedAgents simulates a zut restart by spawning | |
| 67 | 67 | // in one Supervisor, throwing it away, then opening a fresh Supervisor against | |
| 68 | 68 | // the same root and calling Reload. The user-visible state — id, | |
| 69 | 69 | // task, branch, dir — must come back, and status must be Detached so | |
| @@ -873,7 +873,7 @@ func TestSpawnReqPersistsModel(t *testing.T) { | |||
| 873 | 873 | // updating this assertion is the failure mode this catches. | |
| 874 | 874 | func TestSubagentWorkerArgsIncludesModelFlags(t *testing.T) { | |
| 875 | 875 | args := subagentWorkerArgs(subagentWorkerArgsOpts{ | |
| 876 | - | Exe: "/zot", Dir: "/wt", SessionPath: "/s.json", InboxPath: "/in.sock", | |
| 876 | + | Exe: "/zut", Dir: "/wt", SessionPath: "/s.json", InboxPath: "/in.sock", | |
| 877 | 877 | Task: "do x", Model: "gpt-5", Provider: "openai", | |
| 878 | 878 | }) | |
| 879 | 879 | want := []string{"--model", "gpt-5", "--provider", "openai"} | |
| @@ -1089,7 +1089,7 @@ func TestActiveSessionScopesSnapshotAll(t *testing.T) { | |||
| 1089 | 1089 | // Supervisor instance is still SessionID="sess-A" after a fresh New + | |
| 1090 | 1090 | // Reload reads it back from meta.json. Without persistence, the | |
| 1091 | 1091 | // scope filter would forget which session owned each agent after | |
| 1092 | - | // a zot restart and the dashboard would leak everything again. | |
| 1092 | + | // a zut restart and the dashboard would leak everything again. | |
| 1093 | 1093 | func TestSessionIDPersistsAcrossReload(t *testing.T) { | |
| 1094 | 1094 | root := t.TempDir() | |
| 1095 | 1095 | mkSupervisor := func() *Supervisor { | |
| @@ -1134,7 +1134,7 @@ func TestSessionIDPersistsAcrossReload(t *testing.T) { | |||
| 1134 | 1134 | // were spawned without an active session, e.g. via a test rig or | |
| 1135 | 1135 | // scripted caller) carry SessionID == "" and remain visible from | |
| 1136 | 1136 | // every scope. Otherwise the schema bump would orphan every | |
| 1137 | - | // pre-existing agent the moment a user upgraded zot. | |
| 1137 | + | // pre-existing agent the moment a user upgraded zut. | |
| 1138 | 1138 | func TestEmptySessionIDIsVisibleFromAnyScope(t *testing.T) { | |
| 1139 | 1139 | root := t.TempDir() | |
| 1140 | 1140 | f := New(Config{ | |
M packages/agent/subagents/process_unix.go +1 -1
| @@ -39,7 +39,7 @@ func closeWorkerPipes(pipes []io.Closer) { | |||
| 39 | 39 | } | |
| 40 | 40 | ||
| 41 | 41 | // setProcessGroup puts a worker in a process group of its own. This keeps a | |
| 42 | - | // canceled worker from sharing a group with the supervising zot process. | |
| 42 | + | // canceled worker from sharing a group with the supervising zut process. | |
| 43 | 43 | func setProcessGroup(cmd *exec.Cmd) { | |
| 44 | 44 | cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} | |
| 45 | 45 | } | |
M packages/agent/subagents/profiles.go +3 -3
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | // Package subagents discovers named agent profiles that can be selected by | |
| 2 | - | // zot's subagent supervisor. Profiles use the common markdown/frontmatter layout: | |
| 2 | + | // zut's subagent supervisor. Profiles use the common markdown/frontmatter layout: | |
| 3 | 3 | // a YAML-like metadata block followed by the agent's system prompt. | |
| 4 | 4 | // | |
| 5 | 5 | // Discovery prefers explicitly configured directories, then the shared | |
| @@ -180,7 +180,7 @@ func searchDirs(cwd, userHome string) []location { | |||
| 180 | 180 | out = append(out, location{dir: dir, label: label}) | |
| 181 | 181 | } | |
| 182 | 182 | } | |
| 183 | - | if extra := os.Getenv("ZOT_AGENT_PROFILES"); extra != "" { | |
| 183 | + | if extra := os.Getenv("ZUT_AGENT_PROFILES"); extra != "" { | |
| 184 | 184 | for _, dir := range filepath.SplitList(extra) { | |
| 185 | 185 | dir = strings.TrimSpace(dir) | |
| 186 | 186 | if dir != "" && !filepath.IsAbs(dir) && strings.TrimSpace(cwd) != "" { | |
| @@ -192,7 +192,7 @@ func searchDirs(cwd, userHome string) []location { | |||
| 192 | 192 | if userHome != "" { | |
| 193 | 193 | add(filepath.Join(userHome, ".agents", "agents"), "global (agents)") | |
| 194 | 194 | // This is an additional compatibility location. The shared .agents | |
| 195 | - | // path above remains zot's documented default. | |
| 195 | + | // path above remains zut's documented default. | |
| 196 | 196 | add(filepath.Join(userHome, ".pi", "agent", "agents"), "global (pi)") | |
| 197 | 197 | } | |
| 198 | 198 | return out | |
M packages/agent/subagents/profiles_test.go +2 -2
| @@ -79,7 +79,7 @@ Implement the requested change. | |||
| 79 | 79 | func TestDiscoverResolvesConfiguredRelativePathAgainstCWD(t *testing.T) { | |
| 80 | 80 | first := t.TempDir() | |
| 81 | 81 | second := t.TempDir() | |
| 82 | - | t.Setenv("ZOT_AGENT_PROFILES", ".agents") | |
| 82 | + | t.Setenv("ZUT_AGENT_PROFILES", ".agents") | |
| 83 | 83 | writeProfile(t, filepath.Join(first, ".agents", "reviewer.md"), `--- | |
| 84 | 84 | name: reviewer | |
| 85 | 85 | description: First session profile | |
| @@ -113,7 +113,7 @@ Second. | |||
| 113 | 113 | func TestDiscoverConfiguredDirectoryWinsAndRejectsUnsafeNames(t *testing.T) { | |
| 114 | 114 | configured := t.TempDir() | |
| 115 | 115 | home := t.TempDir() | |
| 116 | - | t.Setenv("ZOT_AGENT_PROFILES", configured) | |
| 116 | + | t.Setenv("ZUT_AGENT_PROFILES", configured) | |
| 117 | 117 | writeProfile(t, filepath.Join(configured, "reviewer.md"), `--- | |
| 118 | 118 | name: reviewer | |
| 119 | 119 | description: Configured reviewer | |
M packages/agent/subagents/runner.go +18 -18
| @@ -14,11 +14,11 @@ import ( | |||
| 14 | 14 | "time" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | - | // execRunner spawns `zot --subagent-worker <inbox> --session <path>` in | |
| 17 | + | // execRunner spawns `zut --subagent-worker <inbox> --session <path>` in | |
| 18 | 18 | // Agent.Dir (the shared repository root or an isolated worktree) and consumes | |
| 19 | 19 | // its JSONL event stream on stdout. | |
| 20 | 20 | // | |
| 21 | - | // Why a long-lived daemon and not `zot --print`: the supervisor and | |
| 21 | + | // Why a long-lived daemon and not `zut --print`: the supervisor and | |
| 22 | 22 | // the user expect agents to keep accepting follow-up prompts. A | |
| 23 | 23 | // one-shot subprocess can't do that; this design gives each subagent | |
| 24 | 24 | // agent a persistent session file plus an inbox socket the parent | |
| @@ -31,13 +31,13 @@ import ( | |||
| 31 | 31 | // | |
| 32 | 32 | // The on-disk log is the durable record. The Sink updates are an | |
| 33 | 33 | // in-memory mirror so the dashboard doesn't have to tail the file | |
| 34 | - | // for the parent's own agents. /subagents open in a separate zot would | |
| 34 | + | // for the parent's own agents. /subagents open in a separate zut would | |
| 35 | 35 | // read the log directly. | |
| 36 | 36 | type execRunner struct { | |
| 37 | 37 | agent *Agent | |
| 38 | 38 | resolveCredential func(context.Context, string) (Credential, error) | |
| 39 | 39 | ||
| 40 | - | // Command overrides the default `zot --subagent-worker ...` | |
| 40 | + | // Command overrides the default `zut --subagent-worker ...` | |
| 41 | 41 | // invocation. Tests set this to a fake binary (or `go run` | |
| 42 | 42 | // against a tiny stub program) so the supervisor logic can be | |
| 43 | 43 | // tested without a real child. Production code leaves it nil. | |
| @@ -48,7 +48,7 @@ type execRunner struct { | |||
| 48 | 48 | // with <subagent-root>/agents/<id>/session.json. Tests that | |
| 49 | 49 | // hand-build an Agent without going through Spawn must set | |
| 50 | 50 | // one of the two; the runner refuses to invent a fallback | |
| 51 | - | // because the only plausible one (<Dir>/.zot/session.json) | |
| 51 | + | // because the only plausible one (<Dir>/.zut/session.json) | |
| 52 | 52 | // would litter the user's repo — every agent's Dir points | |
| 53 | 53 | // at it directly. | |
| 54 | 54 | SessionPath string | |
| @@ -57,7 +57,7 @@ type execRunner struct { | |||
| 57 | 57 | // subagentWorkerArgsOpts captures every dynamic input to subagentWorkerArgs. | |
| 58 | 58 | // The fields map 1:1 onto child CLI flags; empty values omit the flag | |
| 59 | 59 | // entirely and let the child resolve a default the same way a normal | |
| 60 | - | // `zot` invocation does. | |
| 60 | + | // `zut` invocation does. | |
| 61 | 61 | type subagentWorkerArgsOpts struct { | |
| 62 | 62 | Exe string | |
| 63 | 63 | Dir string | |
| @@ -164,7 +164,7 @@ func subagentWorkerArgs(opts subagentWorkerArgsOpts) []string { | |||
| 164 | 164 | return args | |
| 165 | 165 | } | |
| 166 | 166 | ||
| 167 | - | // resolveSubagentExecutable finds a runnable zot binary without assuming a | |
| 167 | + | // resolveSubagentExecutable finds a runnable zut binary without assuming a | |
| 168 | 168 | // particular installation directory. A parent process can outlive the file | |
| 169 | 169 | // it was started from (for example after a reinstall), so all known | |
| 170 | 170 | // candidates are checked concurrently and the first successful lookup wins. | |
| @@ -207,10 +207,10 @@ func resolveSubagentExecutable(ctx context.Context, self, argv0 string, lookPath | |||
| 207 | 207 | if argv0 != "" { | |
| 208 | 208 | addCandidate(filepath.Base(argv0)) | |
| 209 | 209 | } | |
| 210 | - | addCandidate("zot") | |
| 210 | + | addCandidate("zut") | |
| 211 | 211 | ||
| 212 | 212 | if len(candidates) == 0 { | |
| 213 | - | return "", fmt.Errorf("locate zot executable: no executable candidates") | |
| 213 | + | return "", fmt.Errorf("locate zut executable: no executable candidates") | |
| 214 | 214 | } | |
| 215 | 215 | ||
| 216 | 216 | type lookupResult struct { | |
| @@ -240,7 +240,7 @@ func resolveSubagentExecutable(ctx context.Context, self, argv0 string, lookPath | |||
| 240 | 240 | } | |
| 241 | 241 | lookupErrors = append(lookupErrors, fmt.Sprintf("%q: %v", result.candidate, result.err)) | |
| 242 | 242 | } | |
| 243 | - | return "", fmt.Errorf("locate zot executable: %s", strings.Join(lookupErrors, "; ")) | |
| 243 | + | return "", fmt.Errorf("locate zut executable: %s", strings.Join(lookupErrors, "; ")) | |
| 244 | 244 | } | |
| 245 | 245 | ||
| 246 | 246 | func (r *execRunner) Run(ctx context.Context, sink Sink) error { | |
| @@ -251,7 +251,7 @@ func (r *execRunner) Run(ctx context.Context, sink Sink) error { | |||
| 251 | 251 | // <subagent-root>/agents/<id>/session.json so the per- | |
| 252 | 252 | // agent state is entirely outside the working tree. | |
| 253 | 253 | // Crucial because Agent.Dir points at the user's repo; | |
| 254 | - | // any .zot/ scratch directory under Dir would litter | |
| 254 | + | // any .zut/ scratch directory under Dir would litter | |
| 255 | 255 | // their source tree. | |
| 256 | 256 | // | |
| 257 | 257 | // There is no third fallback. If neither path is set we | |
| @@ -291,7 +291,7 @@ func (r *execRunner) Run(ctx context.Context, sink Sink) error { | |||
| 291 | 291 | exe, err := resolveSubagentExecutable(ctx, self, argv0, nil) | |
| 292 | 292 | if err != nil { | |
| 293 | 293 | if selfErr != nil { | |
| 294 | - | return fmt.Errorf("locate zot executable (os.Executable: %v): %w", selfErr, err) | |
| 294 | + | return fmt.Errorf("locate zut executable (os.Executable: %v): %w", selfErr, err) | |
| 295 | 295 | } | |
| 296 | 296 | return err | |
| 297 | 297 | } | |
| @@ -301,17 +301,17 @@ func (r *execRunner) Run(ctx context.Context, sink Sink) error { | |||
| 301 | 301 | cmd := exec.CommandContext(ctx, args[0], args[1:]...) | |
| 302 | 302 | cmd.Dir = r.agent.Dir | |
| 303 | 303 | cmd.Env = append(workerEnvironment(r.agent.Provider), | |
| 304 | - | "ZOT_SUBAGENT_AGENT_ID="+r.agent.ID, | |
| 305 | - | "ZOT_SUBAGENT_EVENT_LOG="+logPath, | |
| 304 | + | "ZUT_SUBAGENT_AGENT_ID="+r.agent.ID, | |
| 305 | + | "ZUT_SUBAGENT_EVENT_LOG="+logPath, | |
| 306 | 306 | ) | |
| 307 | 307 | if r.agent.HeartbeatInterval > 0 { | |
| 308 | - | cmd.Env = append(cmd.Env, "ZOT_SUBAGENT_HEARTBEAT_INTERVAL="+r.agent.HeartbeatInterval.String()) | |
| 308 | + | cmd.Env = append(cmd.Env, "ZUT_SUBAGENT_HEARTBEAT_INTERVAL="+r.agent.HeartbeatInterval.String()) | |
| 309 | 309 | } | |
| 310 | 310 | if r.agent.maxOutputBytes > 0 { | |
| 311 | - | cmd.Env = append(cmd.Env, fmt.Sprintf("ZOT_SUBAGENT_MAX_OUTPUT_BYTES=%d", r.agent.maxOutputBytes)) | |
| 311 | + | cmd.Env = append(cmd.Env, fmt.Sprintf("ZUT_SUBAGENT_MAX_OUTPUT_BYTES=%d", r.agent.maxOutputBytes)) | |
| 312 | 312 | } | |
| 313 | 313 | if r.agent.maxOutputLines > 0 { | |
| 314 | - | cmd.Env = append(cmd.Env, fmt.Sprintf("ZOT_SUBAGENT_MAX_OUTPUT_LINES=%d", r.agent.maxOutputLines)) | |
| 314 | + | cmd.Env = append(cmd.Env, fmt.Sprintf("ZUT_SUBAGENT_MAX_OUTPUT_LINES=%d", r.agent.maxOutputLines)) | |
| 315 | 315 | } | |
| 316 | 316 | if r.resolveCredential != nil { | |
| 317 | 317 | credential, resolveErr := r.resolveCredential(ctx, r.agent.Provider) | |
| @@ -324,7 +324,7 @@ func (r *execRunner) Run(ctx context.Context, sink Sink) error { | |||
| 324 | 324 | return fmt.Errorf("encode subagent credential: %w", encodeErr) | |
| 325 | 325 | } | |
| 326 | 326 | cmd.Stdin = bytes.NewReader(encoded) | |
| 327 | - | cmd.Env = append(cmd.Env, "ZOT_SUBAGENT_CREDENTIAL_STDIN=1") | |
| 327 | + | cmd.Env = append(cmd.Env, "ZUT_SUBAGENT_CREDENTIAL_STDIN=1") | |
| 328 | 328 | } | |
| 329 | 329 | } | |
| 330 | 330 | ||
M packages/agent/subagents/runner_e2e_test.go +1 -1
| @@ -40,7 +40,7 @@ func TestRunnerEndToEndWithStubChild(t *testing.T) { | |||
| 40 | 40 | } | |
| 41 | 41 | ||
| 42 | 42 | exe := buildStubChild(t) | |
| 43 | - | t.Setenv("ZOT_STUB_PROTOCOL", "1") | |
| 43 | + | t.Setenv("ZUT_STUB_PROTOCOL", "1") | |
| 44 | 44 | ||
| 45 | 45 | root := t.TempDir() | |
| 46 | 46 | repo := t.TempDir() | |
M packages/agent/subagents/runner_test.go +25 -25
| @@ -20,7 +20,7 @@ import ( | |||
| 20 | 20 | // talk to the model. | |
| 21 | 21 | // | |
| 22 | 22 | // - Forgetting --cwd: the child resolved tools against the parent | |
| 23 | - | // zot's working directory, defeating the whole point of the | |
| 23 | + | // zut's working directory, defeating the whole point of the | |
| 24 | 24 | // worktree isolation. | |
| 25 | 25 | // | |
| 26 | 26 | // - Forgetting --session: a daemon-mode agent without a session | |
| @@ -31,10 +31,10 @@ import ( | |||
| 31 | 31 | // positions. If a flag is renamed, update both the runner and this | |
| 32 | 32 | // test so we notice immediately. | |
| 33 | 33 | func TestCredentialStdinHelperProcess(t *testing.T) { | |
| 34 | - | if os.Getenv("ZOT_SUBAGENT_CREDENTIAL_HELPER") != "1" { | |
| 34 | + | if os.Getenv("ZUT_SUBAGENT_CREDENTIAL_HELPER") != "1" { | |
| 35 | 35 | return | |
| 36 | 36 | } | |
| 37 | - | if os.Getenv("ZOT_SUBAGENT_CREDENTIAL_STDIN") != "1" { | |
| 37 | + | if os.Getenv("ZUT_SUBAGENT_CREDENTIAL_STDIN") != "1" { | |
| 38 | 38 | os.Exit(2) | |
| 39 | 39 | } | |
| 40 | 40 | var credential Credential | |
| @@ -67,7 +67,7 @@ func TestWorkerEnvironmentRedactsProviderSecrets(t *testing.T) { | |||
| 67 | 67 | t.Setenv("CUSTOM_PROVIDER_KEY", "key-secret") | |
| 68 | 68 | t.Setenv("AWS_ACCESS_KEY_ID", "aws-access-secret") | |
| 69 | 69 | t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/tmp/service-account.json") | |
| 70 | - | t.Setenv("ZOT_SUBAGENT_TEST_VALUE", "not-secret") | |
| 70 | + | t.Setenv("ZUT_SUBAGENT_TEST_VALUE", "not-secret") | |
| 71 | 71 | env := strings.Join(workerEnvironment("openai"), "\n") | |
| 72 | 72 | for _, secret := range []string{ | |
| 73 | 73 | "OPENAI_API_KEY=openai-secret", | |
| @@ -83,7 +83,7 @@ func TestWorkerEnvironmentRedactsProviderSecrets(t *testing.T) { | |||
| 83 | 83 | t.Fatalf("worker environment leaked %s", secret) | |
| 84 | 84 | } | |
| 85 | 85 | } | |
| 86 | - | if !strings.Contains(env, "ZOT_SUBAGENT_TEST_VALUE=not-secret") { | |
| 86 | + | if !strings.Contains(env, "ZUT_SUBAGENT_TEST_VALUE=not-secret") { | |
| 87 | 87 | t.Fatal("worker environment dropped unrelated variables") | |
| 88 | 88 | } | |
| 89 | 89 | ||
| @@ -98,7 +98,7 @@ func TestWorkerEnvironmentRedactsProviderSecrets(t *testing.T) { | |||
| 98 | 98 | } | |
| 99 | 99 | ||
| 100 | 100 | func TestExecRunnerTransfersCredentialOnlyOnStdin(t *testing.T) { | |
| 101 | - | t.Setenv("ZOT_SUBAGENT_CREDENTIAL_HELPER", "1") | |
| 101 | + | t.Setenv("ZUT_SUBAGENT_CREDENTIAL_HELPER", "1") | |
| 102 | 102 | root := t.TempDir() | |
| 103 | 103 | a := &Agent{ | |
| 104 | 104 | ID: "credential-test", | |
| @@ -147,7 +147,7 @@ func TestApplyEventPreservesMultilineRolesAndFinalAssistant(t *testing.T) { | |||
| 147 | 147 | ||
| 148 | 148 | func TestSubagentWorkerArgs(t *testing.T) { | |
| 149 | 149 | args := subagentWorkerArgs(subagentWorkerArgsOpts{ | |
| 150 | - | Exe: "/path/to/zot", | |
| 150 | + | Exe: "/path/to/zut", | |
| 151 | 151 | Dir: "/tmp/worktree", | |
| 152 | 152 | SessionPath: "/tmp/state/session.json", | |
| 153 | 153 | InboxPath: "/tmp/state/in.sock", | |
| @@ -158,7 +158,7 @@ func TestSubagentWorkerArgs(t *testing.T) { | |||
| 158 | 158 | if len(args) < 7 { | |
| 159 | 159 | t.Fatalf("argv unexpectedly short: %v", args) | |
| 160 | 160 | } | |
| 161 | - | if args[0] != "/path/to/zot" { | |
| 161 | + | if args[0] != "/path/to/zut" { | |
| 162 | 162 | t.Fatalf("argv[0] = %q; want the binary path", args[0]) | |
| 163 | 163 | } | |
| 164 | 164 | // The task must come last so anything that looks flag-like in | |
| @@ -195,8 +195,8 @@ func TestSubagentWorkerArgs(t *testing.T) { | |||
| 195 | 195 | } | |
| 196 | 196 | ||
| 197 | 197 | func TestResolveSubagentExecutableUsesCurrentWhenAvailable(t *testing.T) { | |
| 198 | - | got, err := resolveSubagentExecutable(context.Background(), "/current/bin/zot", "/old/bin/zot", func(_ context.Context, candidate string) (string, error) { | |
| 199 | - | if candidate == "/current/bin/zot" { | |
| 198 | + | got, err := resolveSubagentExecutable(context.Background(), "/current/bin/zut", "/old/bin/zut", func(_ context.Context, candidate string) (string, error) { | |
| 199 | + | if candidate == "/current/bin/zut" { | |
| 200 | 200 | return candidate, nil | |
| 201 | 201 | } | |
| 202 | 202 | return "", os.ErrNotExist | |
| @@ -204,22 +204,22 @@ func TestResolveSubagentExecutableUsesCurrentWhenAvailable(t *testing.T) { | |||
| 204 | 204 | if err != nil { | |
| 205 | 205 | t.Fatal(err) | |
| 206 | 206 | } | |
| 207 | - | if got != "/current/bin/zot" { | |
| 207 | + | if got != "/current/bin/zut" { | |
| 208 | 208 | t.Fatalf("executable = %q, want current executable", got) | |
| 209 | 209 | } | |
| 210 | 210 | } | |
| 211 | 211 | ||
| 212 | 212 | func TestResolveSubagentExecutableFallsBackToPath(t *testing.T) { | |
| 213 | - | got, err := resolveSubagentExecutable(context.Background(), "/removed/.local/bin/zot", "/removed/.local/bin/zot", func(_ context.Context, candidate string) (string, error) { | |
| 214 | - | if candidate == "zot" { | |
| 215 | - | return "/active/go/bin/zot", nil | |
| 213 | + | got, err := resolveSubagentExecutable(context.Background(), "/removed/.local/bin/zut", "/removed/.local/bin/zut", func(_ context.Context, candidate string) (string, error) { | |
| 214 | + | if candidate == "zut" { | |
| 215 | + | return "/active/go/bin/zut", nil | |
| 216 | 216 | } | |
| 217 | 217 | return "", os.ErrNotExist | |
| 218 | 218 | }) | |
| 219 | 219 | if err != nil { | |
| 220 | 220 | t.Fatal(err) | |
| 221 | 221 | } | |
| 222 | - | if got != "/active/go/bin/zot" { | |
| 222 | + | if got != "/active/go/bin/zut" { | |
| 223 | 223 | t.Fatalf("executable = %q, want PATH fallback", got) | |
| 224 | 224 | } | |
| 225 | 225 | } | |
| @@ -227,16 +227,16 @@ func TestResolveSubagentExecutableFallsBackToPath(t *testing.T) { | |||
| 227 | 227 | func TestResolveSubagentExecutableCancelsOtherLookupsAfterFirstSuccess(t *testing.T) { | |
| 228 | 228 | slowStarted := make(chan struct{}) | |
| 229 | 229 | slowCanceled := make(chan struct{}) | |
| 230 | - | got, err := resolveSubagentExecutable(context.Background(), "/current/bin/zot", "/old/bin/zot", func(ctx context.Context, candidate string) (string, error) { | |
| 230 | + | got, err := resolveSubagentExecutable(context.Background(), "/current/bin/zut", "/old/bin/zut", func(ctx context.Context, candidate string) (string, error) { | |
| 231 | 231 | switch candidate { | |
| 232 | - | case "/current/bin/zot": | |
| 232 | + | case "/current/bin/zut": | |
| 233 | 233 | close(slowStarted) | |
| 234 | 234 | <-ctx.Done() | |
| 235 | 235 | close(slowCanceled) | |
| 236 | 236 | return "", ctx.Err() | |
| 237 | - | case "zot": | |
| 237 | + | case "zut": | |
| 238 | 238 | <-slowStarted | |
| 239 | - | return "/active/go/bin/zot", nil | |
| 239 | + | return "/active/go/bin/zut", nil | |
| 240 | 240 | default: | |
| 241 | 241 | return "", os.ErrNotExist | |
| 242 | 242 | } | |
| @@ -244,7 +244,7 @@ func TestResolveSubagentExecutableCancelsOtherLookupsAfterFirstSuccess(t *testin | |||
| 244 | 244 | if err != nil { | |
| 245 | 245 | t.Fatal(err) | |
| 246 | 246 | } | |
| 247 | - | if got != "/active/go/bin/zot" { | |
| 247 | + | if got != "/active/go/bin/zut" { | |
| 248 | 248 | t.Fatalf("executable = %q, want first successful lookup", got) | |
| 249 | 249 | } | |
| 250 | 250 | select { | |
| @@ -256,7 +256,7 @@ func TestResolveSubagentExecutableCancelsOtherLookupsAfterFirstSuccess(t *testin | |||
| 256 | 256 | ||
| 257 | 257 | func TestSubagentWorkerArgsPropagatesProviderConnectionSettings(t *testing.T) { | |
| 258 | 258 | args := subagentWorkerArgs(subagentWorkerArgsOpts{ | |
| 259 | - | Exe: "/zot", | |
| 259 | + | Exe: "/zut", | |
| 260 | 260 | Dir: "/work", | |
| 261 | 261 | SessionPath: "/state/session.json", | |
| 262 | 262 | InboxPath: "/state/inbox.sock", | |
| @@ -282,7 +282,7 @@ func TestDefaultChildArgsPropagatesProviderConnectionSettings(t *testing.T) { | |||
| 282 | 282 | BaseURL: "https://gateway.example.test/v1", | |
| 283 | 283 | InsecureTLS: true, | |
| 284 | 284 | } | |
| 285 | - | args := defaultChildArgs("/zot", a, "/state/session.json", "/state/inbox.sock") | |
| 285 | + | args := defaultChildArgs("/zut", a, "/state/session.json", "/state/inbox.sock") | |
| 286 | 286 | if i := indexOf(args, "--base-url"); i < 0 || safeAt(args, i+1) != a.BaseURL { | |
| 287 | 287 | t.Fatalf("argv = %v, want Agent.BaseURL propagated", args) | |
| 288 | 288 | } | |
| @@ -296,7 +296,7 @@ func TestDefaultChildArgsPropagatesProviderConnectionSettings(t *testing.T) { | |||
| 296 | 296 | // positional which the arg parser would treat as a real prompt. | |
| 297 | 297 | func TestSubagentWorkerArgsEmptyTaskOmitsPositional(t *testing.T) { | |
| 298 | 298 | args := subagentWorkerArgs(subagentWorkerArgsOpts{ | |
| 299 | - | Exe: "/zot", Dir: "/wt", SessionPath: "/s.json", InboxPath: "/in.sock", | |
| 299 | + | Exe: "/zut", Dir: "/wt", SessionPath: "/s.json", InboxPath: "/in.sock", | |
| 300 | 300 | }) | |
| 301 | 301 | for _, a := range args { | |
| 302 | 302 | if a == "" { | |
| @@ -320,7 +320,7 @@ func TestSubagentWorkerArgsEmptyTaskOmitsPositional(t *testing.T) { | |||
| 320 | 320 | // initial user turn. | |
| 321 | 321 | func TestDefaultChildArgsSpawnIncludesTask(t *testing.T) { | |
| 322 | 322 | a := &Agent{Dir: "/wt", Task: "do thing"} | |
| 323 | - | args := defaultChildArgs("/zot", a, "/s.json", "/in.sock") | |
| 323 | + | args := defaultChildArgs("/zut", a, "/s.json", "/in.sock") | |
| 324 | 324 | if got := args[len(args)-1]; got != "do thing" { | |
| 325 | 325 | t.Fatalf("spawn argv last = %q; want %q\n%v", got, "do thing", args) | |
| 326 | 326 | } | |
| @@ -334,7 +334,7 @@ func TestDefaultChildArgsSpawnIncludesTask(t *testing.T) { | |||
| 334 | 334 | // user types next via the inbox. | |
| 335 | 335 | func TestDefaultChildArgsResumeOmitsTask(t *testing.T) { | |
| 336 | 336 | a := &Agent{Dir: "/wt", Task: "do thing", Resuming: true} | |
| 337 | - | args := defaultChildArgs("/zot", a, "/s.json", "/in.sock") | |
| 337 | + | args := defaultChildArgs("/zut", a, "/s.json", "/in.sock") | |
| 338 | 338 | for _, v := range args { | |
| 339 | 339 | if v == "do thing" { | |
| 340 | 340 | t.Fatalf("resume argv contains the task; it would re-fire as a duplicate turn\n%v", args) | |
M packages/agent/subagents/socketpath.go +3 -3
| @@ -29,7 +29,7 @@ const maxUnixSocketPath = 100 | |||
| 29 | 29 | // 3. /tmp as a Unix fallback. | |
| 30 | 30 | // | |
| 31 | 31 | // Each candidate is probed with a real unix listener before it is selected. | |
| 32 | - | // The root hash keeps separate zot homes from colliding while preserving a | |
| 32 | + | // The root hash keeps separate zut homes from colliding while preserving a | |
| 33 | 33 | // stable path across parent and child processes and across Resume calls. | |
| 34 | 34 | func inboxSocketPath(root, agentID string) (string, error) { | |
| 35 | 35 | if runtime.GOOS == "windows" { | |
| @@ -65,7 +65,7 @@ func inboxSocketPath(root, agentID string) (string, error) { | |||
| 65 | 65 | } | |
| 66 | 66 | ||
| 67 | 67 | func socketPathInBase(base, root, agentID string) (string, error) { | |
| 68 | - | dir := filepath.Join(base, "zot-subagents-"+rootTag(root)) | |
| 68 | + | dir := filepath.Join(base, "zut-subagents-"+rootTag(root)) | |
| 69 | 69 | if err := os.MkdirAll(dir, 0o700); err != nil { | |
| 70 | 70 | return "", fmt.Errorf("socket dir %s: %w", dir, err) | |
| 71 | 71 | } | |
| @@ -110,7 +110,7 @@ func probeUnixSocket(dir string) error { | |||
| 110 | 110 | } | |
| 111 | 111 | ||
| 112 | 112 | // rootTag returns a stable 8-hex-char tag for the subagent root. Used | |
| 113 | - | // in the runtime-directory name so two parallel zot instances with | |
| 113 | + | // in the runtime-directory name so two parallel zut instances with | |
| 114 | 114 | // different roots don't share sockets. | |
| 115 | 115 | func rootTag(root string) string { return shortHash(root) } | |
| 116 | 116 | ||
M packages/agent/subagents/supervisor.go +7 -7
| @@ -1,13 +1,13 @@ | |||
| 1 | - | // Package subagents implements zot's subagent supervisor. | |
| 1 | + | // Package subagents implements zut's subagent supervisor. | |
| 2 | 2 | // | |
| 3 | - | // A Supervisor manages headless, long-lived zot subprocesses and keeps their | |
| 3 | + | // A Supervisor manages headless, long-lived zut subprocesses and keeps their | |
| 4 | 4 | // process/turn state, event logs, sessions, results, and optional worktree | |
| 5 | 5 | // artifacts durable across supervisor restarts. | |
| 6 | 6 | // | |
| 7 | 7 | // Shared workspaces preserve the historical behavior, while an explicit | |
| 8 | 8 | // worktree request gives a child an isolated checkout whose patch is captured | |
| 9 | 9 | // before cleanup. The Runner abstraction lets tests inject a fake process; | |
| 10 | - | // production uses `zot --subagent-worker ...`. | |
| 10 | + | // production uses `zut --subagent-worker ...`. | |
| 11 | 11 | package subagents | |
| 12 | 12 | ||
| 13 | 13 | import ( | |
| @@ -43,7 +43,7 @@ type Config struct { | |||
| 43 | 43 | Context context.Context | |
| 44 | 44 | ||
| 45 | 45 | // Root is the directory under which per-agent state files live. | |
| 46 | - | // Typically <ZotHome>/subagents, but tests pass a tempdir. | |
| 46 | + | // Typically <ZutHome>/subagents, but tests pass a tempdir. | |
| 47 | 47 | Root string | |
| 48 | 48 | ||
| 49 | 49 | // Policy owns concurrency, timeout, output, and path safety limits. | |
| @@ -70,7 +70,7 @@ type Config struct { | |||
| 70 | 70 | InsecureTLS bool | |
| 71 | 71 | ||
| 72 | 72 | // NewRunner produces the Runner for an Agent. If nil, the default | |
| 73 | - | // `zot --subagent-worker ...` exec runner is used. Tests inject a fake | |
| 73 | + | // `zut --subagent-worker ...` exec runner is used. Tests inject a fake | |
| 74 | 74 | // here. | |
| 75 | 75 | NewRunner func(a *Agent) Runner | |
| 76 | 76 | ||
| @@ -254,7 +254,7 @@ func (f *Supervisor) ProviderSettings() (string, bool) { | |||
| 254 | 254 | } | |
| 255 | 255 | ||
| 256 | 256 | // SetActiveSession scopes the dashboard view (and Spawn stamping) | |
| 257 | - | // to a particular host zot session id. Pass empty to clear the | |
| 257 | + | // to a particular host zut session id. Pass empty to clear the | |
| 258 | 258 | // scope and revert to "show every agent" (the original behaviour). | |
| 259 | 259 | // | |
| 260 | 260 | // Existing in-memory agents keep their SessionID; only the filter | |
| @@ -422,7 +422,7 @@ func (f *Supervisor) SpawnReq(ctx context.Context, req SpawnRequest) (*Agent, er | |||
| 422 | 422 | logPath := filepath.Join(stateDir, "events.jsonl") | |
| 423 | 423 | sessionPath := filepath.Join(stateDir, "session.json") | |
| 424 | 424 | // Keep the transient unix socket outside the durable state root. | |
| 425 | - | // ZOT_HOME may live on a shared or network filesystem that supports | |
| 425 | + | // ZUT_HOME may live on a shared or network filesystem that supports | |
| 426 | 426 | // regular state files but cannot host unix socket nodes. | |
| 427 | 427 | inboxPath, err := inboxSocketPath(f.cfg.Root, id) | |
| 428 | 428 | if err != nil { | |
M packages/agent/subagents/supervisor_test.go +1 -1
| @@ -63,7 +63,7 @@ func TestSpawnRunsAndCompletes(t *testing.T) { | |||
| 63 | 63 | } | |
| 64 | 64 | ||
| 65 | 65 | // TestSpawnAgentSharesRepoRoot verifies the only-mode-we-support: | |
| 66 | - | // every spawned agent points its cwd at the parent zot's RepoRoot. | |
| 66 | + | // every spawned agent points its cwd at the parent zut's RepoRoot. | |
| 67 | 67 | func TestSpawnAgentSharesRepoRoot(t *testing.T) { | |
| 68 | 68 | f := newTestSupervisor(t, func(a *Agent) Runner { | |
| 69 | 69 | return RunnerFunc(func(ctx context.Context, sink Sink) error { return nil }) | |
M packages/agent/subagents/testdata/cmd/stubchild/main.go +4 -4
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | // stubchild is a minimal subagent-worker stand-in used by the runner | |
| 2 | - | // end-to-end test. It speaks the daemon protocol the real zot | |
| 2 | + | // end-to-end test. It speaks the daemon protocol the real zut | |
| 3 | 3 | // binary will implement next: | |
| 4 | 4 | // | |
| 5 | 5 | // - parses --subagent-worker <path>, --session <path>, --cwd <path>, | |
| @@ -14,7 +14,7 @@ | |||
| 14 | 14 | // | |
| 15 | 15 | // The runner test compiles this binary into a tempdir, points | |
| 16 | 16 | // subagent's execRunner at it via Command, and asserts the events | |
| 17 | - | // flow through correctly without needing the full zot model | |
| 17 | + | // flow through correctly without needing the full zut model | |
| 18 | 18 | // machinery. | |
| 19 | 19 | package main | |
| 20 | 20 | ||
| @@ -28,7 +28,7 @@ import ( | |||
| 28 | 28 | "sync" | |
| 29 | 29 | "time" | |
| 30 | 30 | ||
| 31 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 31 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 32 | 32 | ) | |
| 33 | 33 | ||
| 34 | 34 | func main() { | |
| @@ -134,7 +134,7 @@ type emitter = func(string, map[string]any) | |||
| 134 | 134 | func newEmitter() emitter { | |
| 135 | 135 | var mu sync.Mutex | |
| 136 | 136 | enc := json.NewEncoder(os.Stdout) | |
| 137 | - | versioned := os.Getenv("ZOT_STUB_PROTOCOL") == "1" | |
| 137 | + | versioned := os.Getenv("ZUT_STUB_PROTOCOL") == "1" | |
| 138 | 138 | return func(typ string, data map[string]any) { | |
| 139 | 139 | mu.Lock() | |
| 140 | 140 | defer mu.Unlock() | |
M packages/agent/systemprompt.go +8 -8
| @@ -24,7 +24,7 @@ type SystemPromptOpts struct { | |||
| 24 | 24 | Custom string // if set, replaces the default identity entirely | |
| 25 | 25 | Append []string // extra text appended at the end | |
| 26 | 26 | Now time.Time | |
| 27 | - | ZotDocsDir string | |
| 27 | + | ZutDocsDir string | |
| 28 | 28 | } | |
| 29 | 29 | ||
| 30 | 30 | // BuildSystemPrompt constructs the system prompt. | |
| @@ -33,7 +33,7 @@ type SystemPromptOpts struct { | |||
| 33 | 33 | // the cached prefix on every request, so bloat is cumulatively | |
| 34 | 34 | // expensive. We ship only: | |
| 35 | 35 | // | |
| 36 | - | // - A one-paragraph identity (who zot is, what the name means, | |
| 36 | + | // - A one-paragraph identity (who zut is, what the name means, | |
| 37 | 37 | // what the TUI expects for output format). | |
| 38 | 38 | // - The date + cwd footer so the model has current-context. | |
| 39 | 39 | // | |
| @@ -45,7 +45,7 @@ type SystemPromptOpts struct { | |||
| 45 | 45 | // | |
| 46 | 46 | // Users who want extra biasing can use --system-prompt (replace), | |
| 47 | 47 | // --append-system-prompt (additive, repeatable), or drop a | |
| 48 | - | // SYSTEM.md in $ZOT_HOME that overrides the default identity. | |
| 48 | + | // SYSTEM.md in $ZUT_HOME that overrides the default identity. | |
| 49 | 49 | func BuildSystemPrompt(o SystemPromptOpts) string { | |
| 50 | 50 | if o.Now.IsZero() { | |
| 51 | 51 | o.Now = time.Now() | |
| @@ -64,10 +64,10 @@ func BuildSystemPrompt(o SystemPromptOpts) string { | |||
| 64 | 64 | sb.WriteString(defaultIdentity) | |
| 65 | 65 | } | |
| 66 | 66 | ||
| 67 | - | if strings.TrimSpace(o.ZotDocsDir) != "" { | |
| 68 | - | sb.WriteString("\n\nZot's own docs are installed under ") | |
| 69 | - | sb.WriteString(o.ZotDocsDir) | |
| 70 | - | sb.WriteString("; use the read tool there when you need details about zot RPC, extensions, skills, or built-in behaviour.") | |
| 67 | + | if strings.TrimSpace(o.ZutDocsDir) != "" { | |
| 68 | + | sb.WriteString("\n\nZut's own docs are installed under ") | |
| 69 | + | sb.WriteString(o.ZutDocsDir) | |
| 70 | + | sb.WriteString("; use the read tool there when you need details about zut RPC, extensions, skills, or built-in behaviour.") | |
| 71 | 71 | } | |
| 72 | 72 | ||
| 73 | 73 | for _, a := range o.Append { | |
| @@ -82,7 +82,7 @@ func BuildSystemPrompt(o SystemPromptOpts) string { | |||
| 82 | 82 | return sb.String() | |
| 83 | 83 | } | |
| 84 | 84 | ||
| 85 | - | const defaultIdentity = `You are an expert coding assistant operating inside zot, a coding agent harness. The name "zot" stands for "zero-overhead-tool"; if the user asks what zot means, answer exactly that. | |
| 85 | + | const defaultIdentity = `You are an expert coding assistant operating inside zut, a coding agent harness. The name "zut" stands for "zero-overhead-tool"; if the user asks what zut means, answer exactly that. | |
| 86 | 86 | ||
| 87 | 87 | Your output renders in a TUI that understands markdown for prose and plain text for tool output. Use markdown freely, keep answers concise, and let tool calls speak for themselves rather than narrating them in prose before you invoke them. Act first, then summarise what you did. | |
| 88 | 88 | ||
M packages/agent/tools/bash.go +3 -3
| @@ -15,8 +15,8 @@ import ( | |||
| 15 | 15 | "strings" | |
| 16 | 16 | "time" | |
| 17 | 17 | ||
| 18 | - | "github.com/patriceckhart/zot/packages/core" | |
| 19 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 18 | + | "github.com/bnema/zut/packages/core" | |
| 19 | + | "github.com/bnema/zut/packages/provider" | |
| 20 | 20 | ) | |
| 21 | 21 | ||
| 22 | 22 | const ( | |
| @@ -224,7 +224,7 @@ func humanDuration(d time.Duration) string { | |||
| 224 | 224 | func writeFullOutput(s string) string { | |
| 225 | 225 | b := make([]byte, 6) | |
| 226 | 226 | _, _ = rand.Read(b) | |
| 227 | - | name := filepath.Join(os.TempDir(), "zot-bash-"+hex.EncodeToString(b)+".log") | |
| 227 | + | name := filepath.Join(os.TempDir(), "zut-bash-"+hex.EncodeToString(b)+".log") | |
| 228 | 228 | if err := os.WriteFile(name, []byte(s), 0o600); err != nil { | |
| 229 | 229 | return "" | |
| 230 | 230 | } | |
M packages/agent/tools/edit.go +3 -3
| @@ -8,9 +8,9 @@ import ( | |||
| 8 | 8 | "os" | |
| 9 | 9 | "strings" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/lsp" | |
| 12 | - | "github.com/patriceckhart/zot/packages/core" | |
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/agent/lsp" | |
| 12 | + | "github.com/bnema/zut/packages/core" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | // EditTool applies one or more exact-match replacements to a file. | |
M packages/agent/tools/lsp.go +3 -3
| @@ -10,9 +10,9 @@ import ( | |||
| 10 | 10 | "time" | |
| 11 | 11 | "unicode/utf8" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/agent/lsp" | |
| 14 | - | "github.com/patriceckhart/zot/packages/core" | |
| 15 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/agent/lsp" | |
| 14 | + | "github.com/bnema/zut/packages/core" | |
| 15 | + | "github.com/bnema/zut/packages/provider" | |
| 16 | 16 | ) | |
| 17 | 17 | ||
| 18 | 18 | const maxLSPToolOutput = 60 * 1024 | |
M packages/agent/tools/lsp_diagnostics.go +3 -3
| @@ -6,9 +6,9 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/agent/lsp" | |
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/agent/lsp" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | const writeDiagnosticsTimeout = 2 * time.Second | |
M packages/agent/tools/lsp_test.go +2 -2
| @@ -10,8 +10,8 @@ import ( | |||
| 10 | 10 | "testing" | |
| 11 | 11 | "unicode/utf8" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/agent/lsp" | |
| 14 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/agent/lsp" | |
| 14 | + | "github.com/bnema/zut/packages/provider" | |
| 15 | 15 | ) | |
| 16 | 16 | ||
| 17 | 17 | func TestLSPToolSchemaAndFormatting(t *testing.T) { | |
M packages/agent/tools/permissions.go +1 -1
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | - | // PermissionSet is the local-runtime permission contract for a packaged .zot agent. | |
| 9 | + | // PermissionSet is the local-runtime permission contract for a packaged .zut agent. | |
| 10 | 10 | type PermissionSet struct { | |
| 11 | 11 | FS struct { | |
| 12 | 12 | Read []string `json:"read"` | |
M packages/agent/tools/read.go +4 -4
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // Package tools implements zot's built-in tools: read, write, edit, bash, and lsp. | |
| 1 | + | // Package tools implements zut's built-in tools: read, write, edit, bash, and lsp. | |
| 2 | 2 | package tools | |
| 3 | 3 | ||
| 4 | 4 | import ( | |
| @@ -11,8 +11,8 @@ import ( | |||
| 11 | 11 | "path/filepath" | |
| 12 | 12 | "strings" | |
| 13 | 13 | ||
| 14 | - | "github.com/patriceckhart/zot/packages/core" | |
| 15 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 14 | + | "github.com/bnema/zut/packages/core" | |
| 15 | + | "github.com/bnema/zut/packages/provider" | |
| 16 | 16 | ) | |
| 17 | 17 | ||
| 18 | 18 | const ( | |
| @@ -195,7 +195,7 @@ func imageMIME(path string) string { | |||
| 195 | 195 | // returns the real media type, independent of the file's extension. | |
| 196 | 196 | // Providers validate the declared media type against the actual bytes | |
| 197 | 197 | // and 400 the whole request on a mismatch, so the extension can never | |
| 198 | - | // be trusted. Returns "" when the format is not one zot ships images | |
| 198 | + | // be trusted. Returns "" when the format is not one zut ships images | |
| 199 | 199 | // for, leaving the caller's extension-based guess in place. | |
| 200 | 200 | func sniffImageMIME(data []byte) string { | |
| 201 | 201 | switch { | |
M packages/agent/tools/subagent_spawn.go +3 -3
| @@ -7,9 +7,9 @@ import ( | |||
| 7 | 7 | "strings" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 11 | - | "github.com/patriceckhart/zot/packages/core" | |
| 12 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 11 | + | "github.com/bnema/zut/packages/core" | |
| 12 | + | "github.com/bnema/zut/packages/provider" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | // SubagentSpawnTool lets the main agent fork a background sub-agent | |
M packages/agent/tools/subagent_spawn_test.go +2 -2
| @@ -8,8 +8,8 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 12 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 12 | + | "github.com/bnema/zut/packages/provider" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | type noopSupervisorRunner struct{} | |
M packages/agent/tools/subagent_status.go +3 -3
| @@ -8,9 +8,9 @@ import ( | |||
| 8 | 8 | "time" | |
| 9 | 9 | "unicode/utf8" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 12 | - | "github.com/patriceckhart/zot/packages/core" | |
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 12 | + | "github.com/bnema/zut/packages/core" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | // SubagentStatusTool reports live state for background sub-agents without | |
M packages/agent/tools/subagent_status_test.go +1 -1
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | 9 | "testing" | |
| 10 | 10 | "time" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/agent/subagents" | |
| 12 | + | "github.com/bnema/zut/packages/agent/subagents" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | type statusTestRunner struct { | |
M packages/agent/tools/telegram_send.go +2 -2
| @@ -6,8 +6,8 @@ import ( | |||
| 6 | 6 | "fmt" | |
| 7 | 7 | "os" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/core" | |
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/core" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // TelegramSender is the small affordance the telegram-send tools call | |
M packages/agent/tools/tools_test.go +1 -1
| @@ -10,7 +10,7 @@ import ( | |||
| 10 | 10 | "strings" | |
| 11 | 11 | "testing" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | func mustJSON(t *testing.T, v any) json.RawMessage { | |
M packages/agent/tools/write.go +3 -3
| @@ -8,9 +8,9 @@ import ( | |||
| 8 | 8 | "path/filepath" | |
| 9 | 9 | "strings" | |
| 10 | 10 | ||
| 11 | - | "github.com/patriceckhart/zot/packages/agent/lsp" | |
| 12 | - | "github.com/patriceckhart/zot/packages/core" | |
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 11 | + | "github.com/bnema/zut/packages/agent/lsp" | |
| 12 | + | "github.com/bnema/zut/packages/core" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | // WriteTool writes content to a file, creating parent directories. | |
M packages/agent/update.go +8 -8
| @@ -14,7 +14,7 @@ import ( | |||
| 14 | 14 | ||
| 15 | 15 | // updateCheckTTL is how often we hit the GitHub API to look for a new | |
| 16 | 16 | // release. Half a day is frequent enough to notice the same day a | |
| 17 | - | // release ships without spamming the API on every zot launch. | |
| 17 | + | // release ships without spamming the API on every zut launch. | |
| 18 | 18 | const updateCheckTTL = 12 * time.Hour | |
| 19 | 19 | ||
| 20 | 20 | // updateCheckFile is the on-disk cache, keyed to the current binary | |
| @@ -24,7 +24,7 @@ const updateCheckFile = "update-check.json" | |||
| 24 | 24 | ||
| 25 | 25 | // githubReleasesAPI is the REST endpoint we query. Using the API (not | |
| 26 | 26 | // the HTML redirect) because the JSON response is stable and small. | |
| 27 | - | const githubReleasesAPI = "https://api.github.com/repos/patriceckhart/zot/releases/latest" | |
| 27 | + | const githubReleasesAPI = "https://api.github.com/repos/bnema/zut/releases/latest" | |
| 28 | 28 | ||
| 29 | 29 | const devVersionNotice = "dev version detected; skipping update check" | |
| 30 | 30 | ||
| @@ -46,7 +46,7 @@ func isDevVersion(version string) bool { | |||
| 46 | 46 | version == "0.0.0-dev" || strings.HasPrefix(version, "0.0.0-dev+") | |
| 47 | 47 | } | |
| 48 | 48 | ||
| 49 | - | // updateCache is the on-disk structure written to $ZOT_HOME. | |
| 49 | + | // updateCache is the on-disk structure written to $ZUT_HOME. | |
| 50 | 50 | type updateCache struct { | |
| 51 | 51 | CheckedAt time.Time `json:"checked_at"` | |
| 52 | 52 | // The version that was current when we last checked. Invalidates | |
| @@ -63,14 +63,14 @@ type updateCache struct { | |||
| 63 | 63 | // Always returns a usable UpdateInfo (zero-value on error). The | |
| 64 | 64 | // banner renderer skips the display when Available is false, so a | |
| 65 | 65 | // network failure silently no-ops; we never block startup on this. | |
| 66 | - | func CheckForUpdate(ctx context.Context, zotHome, currentVersion string) UpdateInfo { | |
| 66 | + | func CheckForUpdate(ctx context.Context, zutHome, currentVersion string) UpdateInfo { | |
| 67 | 67 | // Development builds never have an update to offer. Skip without | |
| 68 | 68 | // reading the cache or touching the network. | |
| 69 | 69 | if isDevVersion(currentVersion) { | |
| 70 | 70 | return UpdateInfo{} | |
| 71 | 71 | } | |
| 72 | 72 | ||
| 73 | - | cachePath := filepath.Join(zotHome, updateCheckFile) | |
| 73 | + | cachePath := filepath.Join(zutHome, updateCheckFile) | |
| 74 | 74 | if c, ok := readUpdateCache(cachePath); ok { | |
| 75 | 75 | // Cache is fresh and tracks the same binary version. | |
| 76 | 76 | // Additional guard: only trust the cache when it already | |
| @@ -111,13 +111,13 @@ func CheckForUpdate(ctx context.Context, zotHome, currentVersion string) UpdateI | |||
| 111 | 111 | // CheckForUpdateAsync runs CheckForUpdate in a goroutine, delivers the | |
| 112 | 112 | // result to the returned channel, and never blocks startup. The | |
| 113 | 113 | // channel is always closed; receivers should `ok`-check. | |
| 114 | - | func CheckForUpdateAsync(zotHome, currentVersion string) <-chan UpdateInfo { | |
| 114 | + | func CheckForUpdateAsync(zutHome, currentVersion string) <-chan UpdateInfo { | |
| 115 | 115 | ch := make(chan UpdateInfo, 1) | |
| 116 | 116 | go func() { | |
| 117 | 117 | defer close(ch) | |
| 118 | 118 | ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) | |
| 119 | 119 | defer cancel() | |
| 120 | - | ch <- CheckForUpdate(ctx, zotHome, currentVersion) | |
| 120 | + | ch <- CheckForUpdate(ctx, zutHome, currentVersion) | |
| 121 | 121 | }() | |
| 122 | 122 | return ch | |
| 123 | 123 | } | |
| @@ -133,7 +133,7 @@ func buildInfo(current, latest, url string) UpdateInfo { | |||
| 133 | 133 | } | |
| 134 | 134 | ||
| 135 | 135 | // versionLess returns a < b for dotted semver-ish tags like "0.0.4". | |
| 136 | - | // Non-numeric components compare as zero, which is fine for zot's | |
| 136 | + | // Non-numeric components compare as zero, which is fine for zut's | |
| 137 | 137 | // x.y.z-only scheme. | |
| 138 | 138 | func versionLess(a, b string) bool { | |
| 139 | 139 | as := splitVersion(a) | |
M packages/agent/updatecmd.go +36 -36
| @@ -16,7 +16,7 @@ import ( | |||
| 16 | 16 | "time" | |
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | - | // runUpdateCommand dispatches `zot update`. Returns (handled=true, err) | |
| 19 | + | // runUpdateCommand dispatches `zut update`. Returns (handled=true, err) | |
| 20 | 20 | // if rawArgs starts with "update"; otherwise (handled=false, nil) so | |
| 21 | 21 | // the main router falls through to the regular flag parser. Mirrors | |
| 22 | 22 | // the shape of runBotCommand / runExtCommand on purpose: identical | |
| @@ -31,7 +31,7 @@ import ( | |||
| 31 | 31 | // name template defined in .goreleaser.yaml. | |
| 32 | 32 | // 3. Downloads checksums.txt and the asset to a temp directory. | |
| 33 | 33 | // 4. Verifies the asset's sha256 against checksums.txt. | |
| 34 | - | // 5. Extracts the zot binary from the archive. | |
| 34 | + | // 5. Extracts the zut binary from the archive. | |
| 35 | 35 | // 6. Atomically replaces the running binary with the new one. | |
| 36 | 36 | // | |
| 37 | 37 | // Refuses to operate on development builds because there is no meaningful | |
| @@ -58,25 +58,25 @@ func runUpdateCommand(rawArgs []string, version string) (handled bool, err error | |||
| 58 | 58 | } | |
| 59 | 59 | ||
| 60 | 60 | func printUpdateHelp() { | |
| 61 | - | fmt.Fprintln(os.Stderr, `zot update — replace the current zot binary with the latest release | |
| 61 | + | fmt.Fprintln(os.Stderr, `zut update — replace the current zut binary with the latest release | |
| 62 | 62 | ||
| 63 | 63 | usage: | |
| 64 | - | zot update download and install the newest release | |
| 65 | - | zot update --check show what update is available; install nothing | |
| 66 | - | zot update --help show this help | |
| 64 | + | zut update download and install the newest release | |
| 65 | + | zut update --check show what update is available; install nothing | |
| 66 | + | zut update --help show this help | |
| 67 | 67 | ||
| 68 | 68 | notes: | |
| 69 | 69 | * The binary must be writable by the current user. On a system-wide | |
| 70 | - | install (e.g. /usr/local/bin/zot owned by root) re-run with sudo. | |
| 70 | + | install (e.g. /usr/local/bin/zut owned by root) re-run with sudo. | |
| 71 | 71 | * Development builds are refused. They typically come from a local | |
| 72 | 72 | source build and shouldn't be silently replaced with a release binary. | |
| 73 | 73 | * Honours $GITHUB_TOKEN if set, so private-repo releases work. | |
| 74 | 74 | * After the binary is installed, every extension under | |
| 75 | - | $ZOT_HOME/extensions/ that is a git checkout is fast-forward | |
| 75 | + | $ZUT_HOME/extensions/ that is a git checkout is fast-forward | |
| 76 | 76 | pulled (no merge, no rebase). Dirty worktrees are stashed and | |
| 77 | 77 | restored. Extensions without a .git directory, disabled | |
| 78 | 78 | extensions, and pulls that fail (offline, diverged, etc.) are | |
| 79 | - | skipped per-extension and never abort the overall update. zot | |
| 79 | + | skipped per-extension and never abort the overall update. zut | |
| 80 | 80 | does NOT run any build step after pulling — authors are expected | |
| 81 | 81 | to commit a working binary, or you can rebuild manually and | |
| 82 | 82 | /reload-ext.`) | |
| @@ -87,7 +87,7 @@ notes: | |||
| 87 | 87 | // pipe into scripts. | |
| 88 | 88 | func runUpdateCheck(version string) error { | |
| 89 | 89 | if isDevVersion(version) { | |
| 90 | - | fmt.Println("zot:", devVersionNotice) | |
| 90 | + | fmt.Println("zut:", devVersionNotice) | |
| 91 | 91 | return nil | |
| 92 | 92 | } | |
| 93 | 93 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | |
| @@ -99,24 +99,24 @@ func runUpdateCheck(version string) error { | |||
| 99 | 99 | latest := strings.TrimPrefix(tag, "v") | |
| 100 | 100 | current := versionOnly(version) | |
| 101 | 101 | if !versionLess(current, latest) { | |
| 102 | - | fmt.Printf("zot %s is up to date (latest: %s)\n", current, latest) | |
| 102 | + | fmt.Printf("zut %s is up to date (latest: %s)\n", current, latest) | |
| 103 | 103 | return nil | |
| 104 | 104 | } | |
| 105 | - | fmt.Printf("zot %s -> %s available\n release: %s\n run 'zot update' to install\n", current, latest, url) | |
| 105 | + | fmt.Printf("zut %s -> %s available\n release: %s\n run 'zut update' to install\n", current, latest, url) | |
| 106 | 106 | return nil | |
| 107 | 107 | } | |
| 108 | 108 | ||
| 109 | - | // runUpdate is the meat of `zot update`. | |
| 109 | + | // runUpdate is the meat of `zut update`. | |
| 110 | 110 | func runUpdate(version string) error { | |
| 111 | 111 | if isDevVersion(version) { | |
| 112 | - | return errors.New(devVersionNotice + "; `zot update` is disabled. Build a release tag or download from https://github.com/patriceckhart/zot/releases") | |
| 112 | + | return errors.New(devVersionNotice + "; `zut update` is disabled. Build a release tag or download from https://github.com/bnema/zut/releases") | |
| 113 | 113 | } | |
| 114 | 114 | current := versionOnly(version) | |
| 115 | 115 | ||
| 116 | 116 | ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) | |
| 117 | 117 | defer cancel() | |
| 118 | 118 | ||
| 119 | - | fmt.Println("zot update: querying latest release...") | |
| 119 | + | fmt.Println("zut update: querying latest release...") | |
| 120 | 120 | tag, releaseURL, err := fetchLatestRelease(ctx) | |
| 121 | 121 | if err != nil { | |
| 122 | 122 | return fmt.Errorf("query latest release: %w", err) | |
| @@ -124,18 +124,18 @@ func runUpdate(version string) error { | |||
| 124 | 124 | latest := strings.TrimPrefix(tag, "v") | |
| 125 | 125 | ||
| 126 | 126 | if !versionLess(current, latest) { | |
| 127 | - | fmt.Printf("zot %s is already up to date.\n", current) | |
| 127 | + | fmt.Printf("zut %s is already up to date.\n", current) | |
| 128 | 128 | return nil | |
| 129 | 129 | } | |
| 130 | - | fmt.Printf("zot update: %s -> %s\n", current, latest) | |
| 131 | - | fmt.Printf("zot update: release page %s\n", releaseURL) | |
| 130 | + | fmt.Printf("zut update: %s -> %s\n", current, latest) | |
| 131 | + | fmt.Printf("zut update: release page %s\n", releaseURL) | |
| 132 | 132 | ||
| 133 | 133 | // Pick the archive matching this platform. | |
| 134 | 134 | assetName, archiveFmt, err := releaseAssetName(latest) | |
| 135 | 135 | if err != nil { | |
| 136 | 136 | return err | |
| 137 | 137 | } | |
| 138 | - | fmt.Printf("zot update: target asset %s\n", assetName) | |
| 138 | + | fmt.Printf("zut update: target asset %s\n", assetName) | |
| 139 | 139 | ||
| 140 | 140 | // We assume the standard GoReleaser layout: assets live under | |
| 141 | 141 | // https://github.com/<owner>/<repo>/releases/download/<tag>/<file> | |
| @@ -146,7 +146,7 @@ func runUpdate(version string) error { | |||
| 146 | 146 | assetURL := base + "/" + assetName | |
| 147 | 147 | sumsURL := base + "/checksums.txt" | |
| 148 | 148 | ||
| 149 | - | tmp, err := os.MkdirTemp("", "zot-update-") | |
| 149 | + | tmp, err := os.MkdirTemp("", "zut-update-") | |
| 150 | 150 | if err != nil { | |
| 151 | 151 | return fmt.Errorf("create temp dir: %w", err) | |
| 152 | 152 | } | |
| @@ -155,7 +155,7 @@ func runUpdate(version string) error { | |||
| 155 | 155 | // users can clear /tmp themselves. | |
| 156 | 156 | defer func() { _ = os.RemoveAll(tmp) }() | |
| 157 | 157 | ||
| 158 | - | fmt.Println("zot update: downloading checksums.txt...") | |
| 158 | + | fmt.Println("zut update: downloading checksums.txt...") | |
| 159 | 159 | sumsPath := filepath.Join(tmp, "checksums.txt") | |
| 160 | 160 | if err := downloadFile(ctx, sumsURL, sumsPath); err != nil { | |
| 161 | 161 | return fmt.Errorf("download checksums: %w", err) | |
| @@ -165,13 +165,13 @@ func runUpdate(version string) error { | |||
| 165 | 165 | return err | |
| 166 | 166 | } | |
| 167 | 167 | ||
| 168 | - | fmt.Println("zot update: downloading archive...") | |
| 168 | + | fmt.Println("zut update: downloading archive...") | |
| 169 | 169 | archivePath := filepath.Join(tmp, assetName) | |
| 170 | 170 | if err := downloadFile(ctx, assetURL, archivePath); err != nil { | |
| 171 | 171 | return fmt.Errorf("download archive: %w", err) | |
| 172 | 172 | } | |
| 173 | 173 | ||
| 174 | - | fmt.Println("zot update: verifying checksum...") | |
| 174 | + | fmt.Println("zut update: verifying checksum...") | |
| 175 | 175 | gotSum, err := sha256File(archivePath) | |
| 176 | 176 | if err != nil { | |
| 177 | 177 | return fmt.Errorf("hash archive: %w", err) | |
| @@ -180,7 +180,7 @@ func runUpdate(version string) error { | |||
| 180 | 180 | return fmt.Errorf("checksum mismatch for %s: got %s, want %s", assetName, gotSum, wantSum) | |
| 181 | 181 | } | |
| 182 | 182 | ||
| 183 | - | fmt.Println("zot update: extracting...") | |
| 183 | + | fmt.Println("zut update: extracting...") | |
| 184 | 184 | extractDir := filepath.Join(tmp, "extracted") | |
| 185 | 185 | if err := os.MkdirAll(extractDir, 0o755); err != nil { | |
| 186 | 186 | return fmt.Errorf("mkdir extract: %w", err) | |
| @@ -189,35 +189,35 @@ func runUpdate(version string) error { | |||
| 189 | 189 | return fmt.Errorf("extract archive: %w", err) | |
| 190 | 190 | } | |
| 191 | 191 | ||
| 192 | - | newBin := filepath.Join(extractDir, "zot") | |
| 192 | + | newBin := filepath.Join(extractDir, "zut") | |
| 193 | 193 | if runtime.GOOS == "windows" { | |
| 194 | - | newBin = filepath.Join(extractDir, "zot.exe") | |
| 194 | + | newBin = filepath.Join(extractDir, "zut.exe") | |
| 195 | 195 | } | |
| 196 | 196 | if st, err := os.Stat(newBin); err != nil || st.IsDir() { | |
| 197 | - | return fmt.Errorf("extracted archive does not contain a zot binary at %s", newBin) | |
| 197 | + | return fmt.Errorf("extracted archive does not contain a zut binary at %s", newBin) | |
| 198 | 198 | } | |
| 199 | 199 | ||
| 200 | 200 | curBin, err := os.Executable() | |
| 201 | 201 | if err != nil { | |
| 202 | 202 | return fmt.Errorf("resolve current binary path: %w", err) | |
| 203 | 203 | } | |
| 204 | - | // Resolve symlinks so 'zot' on $PATH that points at /usr/local/bin | |
| 204 | + | // Resolve symlinks so 'zut' on $PATH that points at /usr/local/bin | |
| 205 | 205 | // gets actually replaced rather than us writing into the symlink | |
| 206 | 206 | // target's directory while leaving the link stale. | |
| 207 | 207 | if resolved, err := filepath.EvalSymlinks(curBin); err == nil { | |
| 208 | 208 | curBin = resolved | |
| 209 | 209 | } | |
| 210 | 210 | ||
| 211 | - | fmt.Printf("zot update: replacing %s\n", curBin) | |
| 211 | + | fmt.Printf("zut update: replacing %s\n", curBin) | |
| 212 | 212 | if err := replaceBinary(curBin, newBin); err != nil { | |
| 213 | 213 | return fmt.Errorf("replace binary: %w", err) | |
| 214 | 214 | } | |
| 215 | - | fmt.Printf("zot update: installed %s\n", latest) | |
| 215 | + | fmt.Printf("zut update: installed %s\n", latest) | |
| 216 | 216 | ||
| 217 | 217 | // Best-effort: also refresh installed extensions that live in | |
| 218 | 218 | // git checkouts. Failures here are advisory and never abort the | |
| 219 | 219 | // overall update — the binary swap already succeeded. | |
| 220 | - | updateAllExtensions(ZotHome()) | |
| 220 | + | updateAllExtensions(ZutHome()) | |
| 221 | 221 | ||
| 222 | 222 | return nil | |
| 223 | 223 | } | |
| @@ -236,13 +236,13 @@ func releaseAssetName(version string) (name, format string, err error) { | |||
| 236 | 236 | case "windows": | |
| 237 | 237 | // supported | |
| 238 | 238 | default: | |
| 239 | - | return "", "", fmt.Errorf("unsupported OS for zot update: %s (download manually from the release page)", goos) | |
| 239 | + | return "", "", fmt.Errorf("unsupported OS for zut update: %s (download manually from the release page)", goos) | |
| 240 | 240 | } | |
| 241 | 241 | switch goarch { | |
| 242 | 242 | case "amd64", "arm64": | |
| 243 | 243 | // supported | |
| 244 | 244 | default: | |
| 245 | - | return "", "", fmt.Errorf("unsupported CPU arch for zot update: %s", goarch) | |
| 245 | + | return "", "", fmt.Errorf("unsupported CPU arch for zut update: %s", goarch) | |
| 246 | 246 | } | |
| 247 | 247 | if goos == "windows" && goarch == "arm64" { | |
| 248 | 248 | return "", "", errors.New("windows/arm64 release artifacts are not published; download manually") | |
| @@ -251,7 +251,7 @@ func releaseAssetName(version string) (name, format string, err error) { | |||
| 251 | 251 | if goos == "windows" { | |
| 252 | 252 | ext = "zip" | |
| 253 | 253 | } | |
| 254 | - | return fmt.Sprintf("zot_%s_%s_%s.%s", version, goos, goarch, ext), ext, nil | |
| 254 | + | return fmt.Sprintf("zut_%s_%s_%s.%s", version, goos, goarch, ext), ext, nil | |
| 255 | 255 | } | |
| 256 | 256 | ||
| 257 | 257 | // downloadFile fetches url to dst, streaming through io.Copy so big | |
| @@ -325,7 +325,7 @@ func sha256File(path string) (string, error) { | |||
| 325 | 325 | } | |
| 326 | 326 | ||
| 327 | 327 | // extractArchive shells out to the system tar / unzip rather than | |
| 328 | - | // pulling in a Go archive lib. Reasoning: zot already depends on | |
| 328 | + | // pulling in a Go archive lib. Reasoning: zut already depends on | |
| 329 | 329 | // system tar implicitly in a few places, every supported platform | |
| 330 | 330 | // ships tar (BSD tar on macOS handles gzip natively, GNU tar on | |
| 331 | 331 | // Linux, bsdtar on Windows 10+), and the dependency-free release | |
| @@ -385,7 +385,7 @@ func replaceBinary(cur, newBin string) error { | |||
| 385 | 385 | return fmt.Errorf("install new binary: %w", err) | |
| 386 | 386 | } | |
| 387 | 387 | // The .old file is locked until this process exits; leave | |
| 388 | - | // it behind. Next `zot update` cleans it up via the | |
| 388 | + | // it behind. Next `zut update` cleans it up via the | |
| 389 | 389 | // os.Remove(bak) above. | |
| 390 | 390 | return nil | |
| 391 | 391 | } | |
R packages/agent/zotfile.go → packages/agent/zutfile.go +120 -120 renamed, 79% similar
| @@ -20,22 +20,22 @@ import ( | |||
| 20 | 20 | "strings" | |
| 21 | 21 | "time" | |
| 22 | 22 | ||
| 23 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 24 | + | "github.com/bnema/zut/packages/provider" | |
| 25 | + | "github.com/bnema/zut/packages/provider/auth" | |
| 26 | + | "github.com/bnema/zut/packages/tui" | |
| 23 | 27 | "github.com/klauspost/compress/zstd" | |
| 24 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 25 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 26 | - | "github.com/patriceckhart/zot/packages/provider/auth" | |
| 27 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 28 | 28 | "golang.org/x/term" | |
| 29 | 29 | ) | |
| 30 | 30 | ||
| 31 | - | type ZotfileManifest struct { | |
| 32 | - | Zotfile int `json:"zotfile"` | |
| 31 | + | type ZutfileManifest struct { | |
| 32 | + | Zutfile int `json:"zutfile"` | |
| 33 | 33 | Name string `json:"name"` | |
| 34 | 34 | Version string `json:"version"` | |
| 35 | 35 | Description string `json:"description"` | |
| 36 | 36 | License string `json:"license"` | |
| 37 | 37 | Runtime struct { | |
| 38 | - | MinZot string `json:"min_zot"` | |
| 38 | + | MinZut string `json:"min_zut"` | |
| 39 | 39 | } `json:"runtime"` | |
| 40 | 40 | Model struct { | |
| 41 | 41 | Requires []string `json:"requires"` | |
| @@ -56,14 +56,14 @@ type ZotfileManifest struct { | |||
| 56 | 56 | ReplaceSystemPrompt bool `json:"replace_system_prompt"` | |
| 57 | 57 | } | |
| 58 | 58 | ||
| 59 | - | type zotfileLoaded struct { | |
| 59 | + | type zutfileLoaded struct { | |
| 60 | 60 | Dir string | |
| 61 | 61 | Temp bool | |
| 62 | 62 | Digest string | |
| 63 | - | Manifest ZotfileManifest | |
| 63 | + | Manifest ZutfileManifest | |
| 64 | 64 | } | |
| 65 | 65 | ||
| 66 | - | func runZotfileCommand(rawArgs []string, version string) (bool, error) { | |
| 66 | + | func runZutfileCommand(rawArgs []string, version string) (bool, error) { | |
| 67 | 67 | if len(rawArgs) == 0 { | |
| 68 | 68 | return false, nil | |
| 69 | 69 | } | |
| @@ -77,17 +77,17 @@ func runZotfileCommand(rawArgs []string, version string) (bool, error) { | |||
| 77 | 77 | if len(rawArgs) > 2 { | |
| 78 | 78 | out = rawArgs[2] | |
| 79 | 79 | } | |
| 80 | - | return true, zotPack(dir, out) | |
| 80 | + | return true, zutPack(dir, out) | |
| 81 | 81 | case "inspect": | |
| 82 | 82 | if len(rawArgs) < 2 { | |
| 83 | - | return true, fmt.Errorf("zot inspect requires a name, .zot file, directory, or GitHub URL") | |
| 83 | + | return true, fmt.Errorf("zut inspect requires a name, .zut file, directory, or GitHub URL") | |
| 84 | 84 | } | |
| 85 | - | return true, zotInspect(rawArgs[1]) | |
| 85 | + | return true, zutInspect(rawArgs[1]) | |
| 86 | 86 | case "verify": | |
| 87 | 87 | if len(rawArgs) < 2 { | |
| 88 | - | return true, fmt.Errorf("zot verify requires a name, .zot file, directory, or GitHub URL") | |
| 88 | + | return true, fmt.Errorf("zut verify requires a name, .zut file, directory, or GitHub URL") | |
| 89 | 89 | } | |
| 90 | - | zf, cleanup, err := loadZotfile(rawArgs[1]) | |
| 90 | + | zf, cleanup, err := loadZutfile(rawArgs[1]) | |
| 91 | 91 | if cleanup != nil { | |
| 92 | 92 | defer cleanup() | |
| 93 | 93 | } | |
| @@ -98,11 +98,11 @@ func runZotfileCommand(rawArgs []string, version string) (bool, error) { | |||
| 98 | 98 | return true, nil | |
| 99 | 99 | case "run": | |
| 100 | 100 | if len(rawArgs) < 2 { | |
| 101 | - | return true, fmt.Errorf("zot run requires a name, .zot file, directory, or GitHub URL") | |
| 101 | + | return true, fmt.Errorf("zut run requires a name, .zut file, directory, or GitHub URL") | |
| 102 | 102 | } | |
| 103 | 103 | yes, remaining := peelLeadingYes(rawArgs[1:]) | |
| 104 | 104 | if len(remaining) < 1 { | |
| 105 | - | return true, fmt.Errorf("zot run requires a name, .zot file, directory, or GitHub URL") | |
| 105 | + | return true, fmt.Errorf("zut run requires a name, .zut file, directory, or GitHub URL") | |
| 106 | 106 | } | |
| 107 | 107 | ref := remaining[0] | |
| 108 | 108 | rest := remaining[1:] | |
| @@ -114,37 +114,37 @@ func runZotfileCommand(rawArgs []string, version string) (bool, error) { | |||
| 114 | 114 | if yes { | |
| 115 | 115 | args.Yes = true | |
| 116 | 116 | } | |
| 117 | - | return true, runLocalZotfile(ref, args, version) | |
| 117 | + | return true, runLocalZutfile(ref, args, version) | |
| 118 | 118 | default: | |
| 119 | 119 | return false, nil | |
| 120 | 120 | } | |
| 121 | 121 | } | |
| 122 | 122 | ||
| 123 | - | func runLocalZotfile(ref string, args Args, version string) error { | |
| 123 | + | func runLocalZutfile(ref string, args Args, version string) error { | |
| 124 | 124 | prepareRuntimeCatalog() | |
| 125 | - | zf, cleanup, err := loadZotfile(ref) | |
| 125 | + | zf, cleanup, err := loadZutfile(ref) | |
| 126 | 126 | if cleanup != nil { | |
| 127 | 127 | defer cleanup() | |
| 128 | 128 | } | |
| 129 | 129 | if err != nil { | |
| 130 | 130 | return err | |
| 131 | 131 | } | |
| 132 | - | if err := checkZotfileRequirements(zf, version); err != nil { | |
| 132 | + | if err := checkZutfileRequirements(zf, version); err != nil { | |
| 133 | 133 | return err | |
| 134 | 134 | } | |
| 135 | - | agentData := filepath.Join(ZotHome(), "agents", safeAgentName(zf.Manifest.Name), "data") | |
| 135 | + | agentData := filepath.Join(ZutHome(), "agents", safeAgentName(zf.Manifest.Name), "data") | |
| 136 | 136 | if err := os.MkdirAll(agentData, 0o755); err != nil { | |
| 137 | 137 | return err | |
| 138 | 138 | } | |
| 139 | 139 | perms := zf.Manifest.Permissions.Expand(args.CWD, agentData) | |
| 140 | - | allowed, err := consentZotfile(zf, perms, args.Yes) | |
| 140 | + | allowed, err := consentZutfile(zf, perms, args.Yes) | |
| 141 | 141 | if err != nil { | |
| 142 | 142 | return err | |
| 143 | 143 | } | |
| 144 | 144 | if !allowed { | |
| 145 | 145 | return nil | |
| 146 | 146 | } | |
| 147 | - | if err := applyZotfileModelRequirements(&args, zf.Manifest); err != nil { | |
| 147 | + | if err := applyZutfileModelRequirements(&args, zf.Manifest); err != nil { | |
| 148 | 148 | return err | |
| 149 | 149 | } | |
| 150 | 150 | agentPath := filepath.Join(zf.Dir, "AGENT.md") | |
| @@ -167,13 +167,13 @@ func runLocalZotfile(ref string, args Args, version string) error { | |||
| 167 | 167 | args.Prompt = *zf.Manifest.Entry.DefaultPrompt | |
| 168 | 168 | } | |
| 169 | 169 | if dirExists(filepath.Join(zf.Dir, "skills")) { | |
| 170 | - | old := os.Getenv("ZOT_AGENT_SKILLS") | |
| 170 | + | old := os.Getenv("ZUT_AGENT_SKILLS") | |
| 171 | 171 | v := filepath.Join(zf.Dir, "skills") | |
| 172 | 172 | if old != "" { | |
| 173 | 173 | v += string(os.PathListSeparator) + old | |
| 174 | 174 | } | |
| 175 | - | _ = os.Setenv("ZOT_AGENT_SKILLS", v) | |
| 176 | - | defer os.Setenv("ZOT_AGENT_SKILLS", old) | |
| 175 | + | _ = os.Setenv("ZUT_AGENT_SKILLS", v) | |
| 176 | + | defer os.Setenv("ZUT_AGENT_SKILLS", old) | |
| 177 | 177 | } | |
| 178 | 178 | args.AgentName = zf.Manifest.Name | |
| 179 | 179 | args.AgentDataDir = agentData | |
| @@ -181,8 +181,8 @@ func runLocalZotfile(ref string, args Args, version string) error { | |||
| 181 | 181 | return runWithArgs(args, version) | |
| 182 | 182 | } | |
| 183 | 183 | ||
| 184 | - | func zotInspect(ref string) error { | |
| 185 | - | zf, cleanup, err := loadZotfile(ref) | |
| 184 | + | func zutInspect(ref string) error { | |
| 185 | + | zf, cleanup, err := loadZutfile(ref) | |
| 186 | 186 | if cleanup != nil { | |
| 187 | 187 | defer cleanup() | |
| 188 | 188 | } | |
| @@ -207,7 +207,7 @@ func zotInspect(ref string) error { | |||
| 207 | 207 | }) | |
| 208 | 208 | } | |
| 209 | 209 | ||
| 210 | - | func applyZotfileModelRequirements(args *Args, m ZotfileManifest) error { | |
| 210 | + | func applyZutfileModelRequirements(args *Args, m ZutfileManifest) error { | |
| 211 | 211 | minCtx := m.Model.MinContext | |
| 212 | 212 | requires := map[string]bool{} | |
| 213 | 213 | for _, capability := range m.Model.Requires { | |
| @@ -219,7 +219,7 @@ func applyZotfileModelRequirements(args *Args, m ZotfileManifest) error { | |||
| 219 | 219 | } | |
| 220 | 220 | } | |
| 221 | 221 | if m.Model.MinTier != "" { | |
| 222 | - | return fmt.Errorf("model.min_tier is not supported by this zot version") | |
| 222 | + | return fmt.Errorf("model.min_tier is not supported by this zut version") | |
| 223 | 223 | } | |
| 224 | 224 | compatible := func(model provider.Model) bool { | |
| 225 | 225 | if model.ContextWindow < minCtx { | |
| @@ -289,27 +289,27 @@ func prepareRuntimeCatalog() { | |||
| 289 | 289 | RefreshModelsAsync() | |
| 290 | 290 | } | |
| 291 | 291 | ||
| 292 | - | func zotPack(dir, out string) error { | |
| 292 | + | func zutPack(dir, out string) error { | |
| 293 | 293 | abs, err := filepath.Abs(dir) | |
| 294 | 294 | if err != nil { | |
| 295 | 295 | return err | |
| 296 | 296 | } | |
| 297 | - | if _, err := readZotManifest(abs); err != nil { | |
| 297 | + | if _, err := readZutManifest(abs); err != nil { | |
| 298 | 298 | return err | |
| 299 | 299 | } | |
| 300 | - | if err := validateZotfileDir(abs); err != nil { | |
| 300 | + | if err := validateZutfileDir(abs); err != nil { | |
| 301 | 301 | return err | |
| 302 | 302 | } | |
| 303 | 303 | if out == "" { | |
| 304 | - | m, _ := readZotManifest(abs) | |
| 304 | + | m, _ := readZutManifest(abs) | |
| 305 | 305 | base := safeAgentName(m.Name) | |
| 306 | 306 | if base == "" { | |
| 307 | 307 | base = filepath.Base(abs) | |
| 308 | 308 | } | |
| 309 | - | out = base + ".zot" | |
| 309 | + | out = base + ".zut" | |
| 310 | 310 | } | |
| 311 | - | if filepath.Ext(out) != ".zot" { | |
| 312 | - | out += ".zot" | |
| 311 | + | if filepath.Ext(out) != ".zut" { | |
| 312 | + | out += ".zut" | |
| 313 | 313 | } | |
| 314 | 314 | outAbs, err := filepath.Abs(out) | |
| 315 | 315 | if err != nil { | |
| @@ -337,53 +337,53 @@ func zotPack(dir, out string) error { | |||
| 337 | 337 | return nil | |
| 338 | 338 | } | |
| 339 | 339 | ||
| 340 | - | func loadZotfile(ref string) (zotfileLoaded, func(), error) { | |
| 341 | - | resolved, err := resolveZotfileRef(ref) | |
| 340 | + | func loadZutfile(ref string) (zutfileLoaded, func(), error) { | |
| 341 | + | resolved, err := resolveZutfileRef(ref) | |
| 342 | 342 | if err != nil { | |
| 343 | - | return zotfileLoaded{}, nil, err | |
| 343 | + | return zutfileLoaded{}, nil, err | |
| 344 | 344 | } | |
| 345 | 345 | ref = resolved | |
| 346 | 346 | if u, err := url.Parse(ref); err == nil && (u.Scheme == "http" || u.Scheme == "https") { | |
| 347 | - | return loadRemoteZotfile(u) | |
| 347 | + | return loadRemoteZutfile(u) | |
| 348 | 348 | } | |
| 349 | 349 | info, err := os.Stat(ref) | |
| 350 | 350 | if err != nil { | |
| 351 | - | return zotfileLoaded{}, nil, err | |
| 351 | + | return zutfileLoaded{}, nil, err | |
| 352 | 352 | } | |
| 353 | 353 | if info.IsDir() { | |
| 354 | 354 | abs, _ := filepath.Abs(ref) | |
| 355 | - | m, err := readZotManifest(abs) | |
| 355 | + | m, err := readZutManifest(abs) | |
| 356 | 356 | if err != nil { | |
| 357 | - | return zotfileLoaded{}, nil, err | |
| 357 | + | return zutfileLoaded{}, nil, err | |
| 358 | 358 | } | |
| 359 | - | if err := validateZotfileDir(abs); err != nil { | |
| 360 | - | return zotfileLoaded{}, nil, err | |
| 359 | + | if err := validateZutfileDir(abs); err != nil { | |
| 360 | + | return zutfileLoaded{}, nil, err | |
| 361 | 361 | } | |
| 362 | - | return zotfileLoaded{Dir: abs, Manifest: m, Digest: digestDirectory(abs)}, nil, nil | |
| 362 | + | return zutfileLoaded{Dir: abs, Manifest: m, Digest: digestDirectory(abs)}, nil, nil | |
| 363 | 363 | } | |
| 364 | - | tmp, err := os.MkdirTemp("", "zotfile-*") | |
| 364 | + | tmp, err := os.MkdirTemp("", "zutfile-*") | |
| 365 | 365 | if err != nil { | |
| 366 | - | return zotfileLoaded{}, nil, err | |
| 366 | + | return zutfileLoaded{}, nil, err | |
| 367 | 367 | } | |
| 368 | 368 | cleanup := func() { _ = os.RemoveAll(tmp) } | |
| 369 | - | digest, err := unpackZotArchive(ref, tmp) | |
| 369 | + | digest, err := unpackZutArchive(ref, tmp) | |
| 370 | 370 | if err != nil { | |
| 371 | 371 | cleanup() | |
| 372 | - | return zotfileLoaded{}, nil, err | |
| 372 | + | return zutfileLoaded{}, nil, err | |
| 373 | 373 | } | |
| 374 | - | m, err := readZotManifest(tmp) | |
| 374 | + | m, err := readZutManifest(tmp) | |
| 375 | 375 | if err != nil { | |
| 376 | 376 | cleanup() | |
| 377 | - | return zotfileLoaded{}, nil, err | |
| 377 | + | return zutfileLoaded{}, nil, err | |
| 378 | 378 | } | |
| 379 | - | if err := validateZotfileDir(tmp); err != nil { | |
| 379 | + | if err := validateZutfileDir(tmp); err != nil { | |
| 380 | 380 | cleanup() | |
| 381 | - | return zotfileLoaded{}, nil, err | |
| 381 | + | return zutfileLoaded{}, nil, err | |
| 382 | 382 | } | |
| 383 | - | return zotfileLoaded{Dir: tmp, Temp: true, Digest: digest, Manifest: m}, cleanup, nil | |
| 383 | + | return zutfileLoaded{Dir: tmp, Temp: true, Digest: digest, Manifest: m}, cleanup, nil | |
| 384 | 384 | } | |
| 385 | 385 | ||
| 386 | - | func resolveZotfileRef(ref string) (string, error) { | |
| 386 | + | func resolveZutfileRef(ref string) (string, error) { | |
| 387 | 387 | if u, err := url.Parse(ref); err == nil && (u.Scheme == "http" || u.Scheme == "https") { | |
| 388 | 388 | return ref, nil | |
| 389 | 389 | } | |
| @@ -393,24 +393,24 @@ func resolveZotfileRef(ref string) (string, error) { | |||
| 393 | 393 | return "", err | |
| 394 | 394 | } | |
| 395 | 395 | if filepath.Ext(ref) == "" { | |
| 396 | - | archive := ref + ".zot" | |
| 396 | + | archive := ref + ".zut" | |
| 397 | 397 | if _, err := os.Stat(archive); err == nil { | |
| 398 | 398 | return archive, nil | |
| 399 | 399 | } else if !os.IsNotExist(err) { | |
| 400 | 400 | return "", err | |
| 401 | 401 | } | |
| 402 | 402 | } | |
| 403 | - | if strings.HasSuffix(strings.ToLower(ref), ".zot") { | |
| 403 | + | if strings.HasSuffix(strings.ToLower(ref), ".zut") { | |
| 404 | 404 | return ref, nil | |
| 405 | 405 | } | |
| 406 | 406 | githubRef := strings.ReplaceAll(ref, "\\", "/") | |
| 407 | 407 | parts := strings.Split(githubRef, "/") | |
| 408 | - | if len(parts) == 2 && validGitHubZotfileSegment(parts[0]) && validGitHubZotfileSegment(parts[1]) { | |
| 408 | + | if len(parts) == 2 && validGitHubZutfileSegment(parts[0]) && validGitHubZutfileSegment(parts[1]) { | |
| 409 | 409 | return "https://github.com/" + parts[0] + "/" + parts[1], nil | |
| 410 | 410 | } | |
| 411 | 411 | if len(parts) >= 3 { | |
| 412 | 412 | for _, part := range parts { | |
| 413 | - | if !validGitHubZotfileSegment(part) { | |
| 413 | + | if !validGitHubZutfileSegment(part) { | |
| 414 | 414 | return ref, nil | |
| 415 | 415 | } | |
| 416 | 416 | } | |
| @@ -419,37 +419,37 @@ func resolveZotfileRef(ref string) (string, error) { | |||
| 419 | 419 | return ref, nil | |
| 420 | 420 | } | |
| 421 | 421 | ||
| 422 | - | func validGitHubZotfileSegment(s string) bool { | |
| 422 | + | func validGitHubZutfileSegment(s string) bool { | |
| 423 | 423 | return s != "" && s == strings.ToLower(s) && safeAgentName(s) == s | |
| 424 | 424 | } | |
| 425 | 425 | ||
| 426 | - | var zotfileHTTPClient = &http.Client{Timeout: 60 * time.Second} | |
| 426 | + | var zutfileHTTPClient = &http.Client{Timeout: 60 * time.Second} | |
| 427 | 427 | ||
| 428 | 428 | var githubArchiveURL = func(owner, repo, ref string) string { | |
| 429 | 429 | return fmt.Sprintf("https://github.com/%s/%s/archive/%s.tar.gz", owner, repo, url.PathEscape(ref)) | |
| 430 | 430 | } | |
| 431 | 431 | ||
| 432 | - | func loadRemoteZotfile(u *url.URL) (zotfileLoaded, func(), error) { | |
| 432 | + | func loadRemoteZutfile(u *url.URL) (zutfileLoaded, func(), error) { | |
| 433 | 433 | if !strings.EqualFold(u.Hostname(), "github.com") { | |
| 434 | - | return zotfileLoaded{}, nil, fmt.Errorf("unsupported zotfile URL host %q; only github.com agent directories are supported", u.Hostname()) | |
| 434 | + | return zutfileLoaded{}, nil, fmt.Errorf("unsupported zutfile URL host %q; only github.com agent directories are supported", u.Hostname()) | |
| 435 | 435 | } | |
| 436 | 436 | owner, repo, ref, subdir, err := parseGitHubAgentURL(u) | |
| 437 | 437 | if err != nil { | |
| 438 | - | return zotfileLoaded{}, nil, err | |
| 438 | + | return zutfileLoaded{}, nil, err | |
| 439 | 439 | } | |
| 440 | - | tmp, err := os.MkdirTemp("", "zotfile-github-*") | |
| 440 | + | tmp, err := os.MkdirTemp("", "zutfile-github-*") | |
| 441 | 441 | if err != nil { | |
| 442 | - | return zotfileLoaded{}, nil, err | |
| 442 | + | return zutfileLoaded{}, nil, err | |
| 443 | 443 | } | |
| 444 | 444 | cleanup := func() { _ = os.RemoveAll(tmp) } | |
| 445 | 445 | if err := downloadGitHubArchive(githubArchiveURL(owner, repo, ref), tmp); err != nil { | |
| 446 | 446 | cleanup() | |
| 447 | - | return zotfileLoaded{}, nil, err | |
| 447 | + | return zutfileLoaded{}, nil, err | |
| 448 | 448 | } | |
| 449 | 449 | root, err := singleExtractedRoot(tmp) | |
| 450 | 450 | if err != nil { | |
| 451 | 451 | cleanup() | |
| 452 | - | return zotfileLoaded{}, nil, err | |
| 452 | + | return zutfileLoaded{}, nil, err | |
| 453 | 453 | } | |
| 454 | 454 | dir := filepath.Join(root, filepath.FromSlash(subdir)) | |
| 455 | 455 | if subdir == "" { | |
| @@ -457,18 +457,18 @@ func loadRemoteZotfile(u *url.URL) (zotfileLoaded, func(), error) { | |||
| 457 | 457 | } | |
| 458 | 458 | if !pathWithin(root, dir) { | |
| 459 | 459 | cleanup() | |
| 460 | - | return zotfileLoaded{}, nil, fmt.Errorf("unsafe GitHub agent path %q", subdir) | |
| 460 | + | return zutfileLoaded{}, nil, fmt.Errorf("unsafe GitHub agent path %q", subdir) | |
| 461 | 461 | } | |
| 462 | - | m, err := readZotManifest(dir) | |
| 462 | + | m, err := readZutManifest(dir) | |
| 463 | 463 | if err != nil { | |
| 464 | 464 | cleanup() | |
| 465 | - | return zotfileLoaded{}, nil, fmt.Errorf("GitHub agent %s/%s/%s: %w", owner, repo, subdir, err) | |
| 465 | + | return zutfileLoaded{}, nil, fmt.Errorf("GitHub agent %s/%s/%s: %w", owner, repo, subdir, err) | |
| 466 | 466 | } | |
| 467 | - | if err := validateZotfileDir(dir); err != nil { | |
| 467 | + | if err := validateZutfileDir(dir); err != nil { | |
| 468 | 468 | cleanup() | |
| 469 | - | return zotfileLoaded{}, nil, err | |
| 469 | + | return zutfileLoaded{}, nil, err | |
| 470 | 470 | } | |
| 471 | - | return zotfileLoaded{Dir: dir, Temp: true, Digest: digestDirectory(dir), Manifest: m}, cleanup, nil | |
| 471 | + | return zutfileLoaded{Dir: dir, Temp: true, Digest: digestDirectory(dir), Manifest: m}, cleanup, nil | |
| 472 | 472 | } | |
| 473 | 473 | ||
| 474 | 474 | func parseGitHubAgentURL(u *url.URL) (owner, repo, ref, subdir string, err error) { | |
| @@ -507,8 +507,8 @@ func downloadGitHubArchive(archiveURL, dst string) error { | |||
| 507 | 507 | if err != nil { | |
| 508 | 508 | return err | |
| 509 | 509 | } | |
| 510 | - | req.Header.Set("User-Agent", "zot") | |
| 511 | - | resp, err := zotfileHTTPClient.Do(req) | |
| 510 | + | req.Header.Set("User-Agent", "zut") | |
| 511 | + | resp, err := zutfileHTTPClient.Do(req) | |
| 512 | 512 | if err != nil { | |
| 513 | 513 | return fmt.Errorf("download GitHub repository: %w", err) | |
| 514 | 514 | } | |
| @@ -516,7 +516,7 @@ func downloadGitHubArchive(archiveURL, dst string) error { | |||
| 516 | 516 | if resp.StatusCode != http.StatusOK { | |
| 517 | 517 | return fmt.Errorf("download GitHub repository: HTTP %d", resp.StatusCode) | |
| 518 | 518 | } | |
| 519 | - | limited := &io.LimitedReader{R: resp.Body, N: maxZotfileCompressedSize + 1} | |
| 519 | + | limited := &io.LimitedReader{R: resp.Body, N: maxZutfileCompressedSize + 1} | |
| 520 | 520 | gr, err := gzip.NewReader(limited) | |
| 521 | 521 | if err != nil { | |
| 522 | 522 | return fmt.Errorf("read GitHub archive: %w", err) | |
| @@ -526,7 +526,7 @@ func downloadGitHubArchive(archiveURL, dst string) error { | |||
| 526 | 526 | return fmt.Errorf("extract GitHub archive: %w", err) | |
| 527 | 527 | } | |
| 528 | 528 | if limited.N <= 0 { | |
| 529 | - | return fmt.Errorf("GitHub archive exceeds %d MiB compressed size limit", maxZotfileCompressedSize>>20) | |
| 529 | + | return fmt.Errorf("GitHub archive exceeds %d MiB compressed size limit", maxZutfileCompressedSize>>20) | |
| 530 | 530 | } | |
| 531 | 531 | return nil | |
| 532 | 532 | } | |
| @@ -547,8 +547,8 @@ func singleExtractedRoot(dir string) (string, error) { | |||
| 547 | 547 | return filepath.Join(dir, entries[0].Name()), nil | |
| 548 | 548 | } | |
| 549 | 549 | ||
| 550 | - | func readZotManifest(dir string) (ZotfileManifest, error) { | |
| 551 | - | var m ZotfileManifest | |
| 550 | + | func readZutManifest(dir string) (ZutfileManifest, error) { | |
| 551 | + | var m ZutfileManifest | |
| 552 | 552 | b, err := os.ReadFile(filepath.Join(dir, "manifest.json")) | |
| 553 | 553 | if err != nil { | |
| 554 | 554 | return m, fmt.Errorf("manifest.json is required: %w", err) | |
| @@ -556,8 +556,8 @@ func readZotManifest(dir string) (ZotfileManifest, error) { | |||
| 556 | 556 | if err := json.Unmarshal(b, &m); err != nil { | |
| 557 | 557 | return m, fmt.Errorf("manifest.json: %w", err) | |
| 558 | 558 | } | |
| 559 | - | if m.Zotfile != 1 { | |
| 560 | - | return m, fmt.Errorf("unsupported zotfile version %d", m.Zotfile) | |
| 559 | + | if m.Zutfile != 1 { | |
| 560 | + | return m, fmt.Errorf("unsupported zutfile version %d", m.Zutfile) | |
| 561 | 561 | } | |
| 562 | 562 | name := strings.TrimSpace(m.Name) | |
| 563 | 563 | if name == "" { | |
| @@ -582,7 +582,7 @@ func readZotManifest(dir string) (ZotfileManifest, error) { | |||
| 582 | 582 | return m, nil | |
| 583 | 583 | } | |
| 584 | 584 | ||
| 585 | - | func validateZotfileDir(dir string) error { | |
| 585 | + | func validateZutfileDir(dir string) error { | |
| 586 | 586 | agentPath := filepath.Join(dir, "AGENT.md") | |
| 587 | 587 | st, err := os.Stat(agentPath) | |
| 588 | 588 | switch { | |
| @@ -602,25 +602,25 @@ func validateZotfileDir(dir string) error { | |||
| 602 | 602 | } | |
| 603 | 603 | ||
| 604 | 604 | const ( | |
| 605 | - | maxZotfileCompressedSize = 100 << 20 | |
| 606 | - | maxZotfileEntrySize = 64 << 20 | |
| 607 | - | maxZotfileExpandedSize = 256 << 20 | |
| 605 | + | maxZutfileCompressedSize = 100 << 20 | |
| 606 | + | maxZutfileEntrySize = 64 << 20 | |
| 607 | + | maxZutfileExpandedSize = 256 << 20 | |
| 608 | 608 | ) | |
| 609 | 609 | ||
| 610 | - | func unpackZotArchive(path, dst string) (string, error) { | |
| 610 | + | func unpackZutArchive(path, dst string) (string, error) { | |
| 611 | 611 | info, err := os.Stat(path) | |
| 612 | 612 | if err != nil { | |
| 613 | 613 | return "", err | |
| 614 | 614 | } | |
| 615 | - | if info.Size() > maxZotfileCompressedSize { | |
| 616 | - | return "", fmt.Errorf("zotfile exceeds %d MiB compressed size limit", maxZotfileCompressedSize>>20) | |
| 615 | + | if info.Size() > maxZutfileCompressedSize { | |
| 616 | + | return "", fmt.Errorf("zutfile exceeds %d MiB compressed size limit", maxZutfileCompressedSize>>20) | |
| 617 | 617 | } | |
| 618 | 618 | b, err := os.ReadFile(path) | |
| 619 | 619 | if err != nil { | |
| 620 | 620 | return "", err | |
| 621 | 621 | } | |
| 622 | 622 | digest := sha256.Sum256(b) | |
| 623 | - | r, err := zstd.NewReader(bytes.NewReader(b), zstd.WithDecoderMaxMemory(maxZotfileExpandedSize)) | |
| 623 | + | r, err := zstd.NewReader(bytes.NewReader(b), zstd.WithDecoderMaxMemory(maxZutfileExpandedSize)) | |
| 624 | 624 | if err != nil { | |
| 625 | 625 | // Development fallback for older experiments. | |
| 626 | 626 | gr, gerr := gzip.NewReader(bytes.NewReader(b)) | |
| @@ -650,7 +650,7 @@ func untar(r io.Reader, dst string) error { | |||
| 650 | 650 | return fmt.Errorf("unsafe path in archive: %s", h.Name) | |
| 651 | 651 | } | |
| 652 | 652 | path := filepath.Join(dst, name) | |
| 653 | - | if h.Size < 0 || h.Size > maxZotfileEntrySize || expanded+h.Size > maxZotfileExpandedSize { | |
| 653 | + | if h.Size < 0 || h.Size > maxZutfileEntrySize || expanded+h.Size > maxZutfileExpandedSize { | |
| 654 | 654 | return fmt.Errorf("archive content exceeds extraction size limit: %s", h.Name) | |
| 655 | 655 | } | |
| 656 | 656 | expanded += h.Size | |
| @@ -699,7 +699,7 @@ func writeCanonicalTar(root string, w io.Writer, exclude ...string) error { | |||
| 699 | 699 | return nil | |
| 700 | 700 | } | |
| 701 | 701 | if d.Type()&os.ModeSymlink != 0 { | |
| 702 | - | return fmt.Errorf("symlinks are not supported in zotfiles: %s", path) | |
| 702 | + | return fmt.Errorf("symlinks are not supported in zutfiles: %s", path) | |
| 703 | 703 | } | |
| 704 | 704 | files = append(files, path) | |
| 705 | 705 | return nil | |
| @@ -747,13 +747,13 @@ func writeCanonicalTar(root string, w io.Writer, exclude ...string) error { | |||
| 747 | 747 | return nil | |
| 748 | 748 | } | |
| 749 | 749 | ||
| 750 | - | func checkZotfileRequirements(zf zotfileLoaded, version string) error { | |
| 751 | - | if min := strings.TrimSpace(zf.Manifest.Runtime.MinZot); min != "" { | |
| 750 | + | func checkZutfileRequirements(zf zutfileLoaded, version string) error { | |
| 751 | + | if min := strings.TrimSpace(zf.Manifest.Runtime.MinZut); min != "" { | |
| 752 | 752 | if isDevVersion(version) { | |
| 753 | - | return fmt.Errorf("agent requires zot %s or newer; unversioned development builds cannot satisfy min_zot", min) | |
| 753 | + | return fmt.Errorf("agent requires zut %s or newer; unversioned development builds cannot satisfy min_zut", min) | |
| 754 | 754 | } | |
| 755 | 755 | if versionLess(version, min) { | |
| 756 | - | return fmt.Errorf("agent requires zot %s or newer; running %s", min, versionOnly(version)) | |
| 756 | + | return fmt.Errorf("agent requires zut %s or newer; running %s", min, versionOnly(version)) | |
| 757 | 757 | } | |
| 758 | 758 | } | |
| 759 | 759 | if len(zf.Manifest.Requirements.OS) > 0 { | |
| @@ -794,14 +794,14 @@ func peelLeadingYes(args []string) (yes bool, rest []string) { | |||
| 794 | 794 | return yes, rest | |
| 795 | 795 | } | |
| 796 | 796 | ||
| 797 | - | func consentZotfile(zf zotfileLoaded, perms tools.PermissionSet, autoYes bool) (bool, error) { | |
| 798 | - | if autoYes || os.Getenv("ZOT_AGENT_CONSENT") == "1" { | |
| 799 | - | return acceptZotfileConsent(zf, perms) | |
| 797 | + | func consentZutfile(zf zutfileLoaded, perms tools.PermissionSet, autoYes bool) (bool, error) { | |
| 798 | + | if autoYes || os.Getenv("ZUT_AGENT_CONSENT") == "1" { | |
| 799 | + | return acceptZutfileConsent(zf, perms) | |
| 800 | 800 | } | |
| 801 | 801 | // "ask" deliberately requires approval on every launch. Other consent | |
| 802 | 802 | // is durable only for this exact artifact digest, so any package change | |
| 803 | 803 | // causes a fresh prompt. | |
| 804 | - | consentPath := filepath.Join(ZotHome(), "agents", safeAgentName(zf.Manifest.Name), "consents", zf.Digest+".json") | |
| 804 | + | consentPath := filepath.Join(ZutHome(), "agents", safeAgentName(zf.Manifest.Name), "consents", zf.Digest+".json") | |
| 805 | 805 | if strings.ToLower(strings.TrimSpace(perms.Bash.Mode)) != "ask" { | |
| 806 | 806 | if _, err := os.Stat(consentPath); err == nil { | |
| 807 | 807 | return true, nil | |
| @@ -811,27 +811,27 @@ func consentZotfile(zf zotfileLoaded, perms tools.PermissionSet, autoYes bool) ( | |||
| 811 | 811 | th := tui.Dark | |
| 812 | 812 | if interactive { | |
| 813 | 813 | cfg, _ := LoadConfig() | |
| 814 | - | th, _, _ = tui.DetectThemeWithCustom(ZotHome(), cfg.Theme, 80*time.Millisecond) | |
| 814 | + | th, _, _ = tui.DetectThemeWithCustom(ZutHome(), cfg.Theme, 80*time.Millisecond) | |
| 815 | 815 | } | |
| 816 | - | fmt.Print(formatZotfileConsent(zf, perms, th, interactive)) | |
| 816 | + | fmt.Print(formatZutfileConsent(zf, perms, th, interactive)) | |
| 817 | 817 | if !interactive { | |
| 818 | - | return false, fmt.Errorf("refusing to run without interactive consent; pass -y/--yes or set ZOT_AGENT_CONSENT=1 to allow") | |
| 818 | + | return false, fmt.Errorf("refusing to run without interactive consent; pass -y/--yes or set ZUT_AGENT_CONSENT=1 to allow") | |
| 819 | 819 | } | |
| 820 | - | allowed, err := readZotfileConsent(os.Stdin, os.Stdout, th) | |
| 820 | + | allowed, err := readZutfileConsent(os.Stdin, os.Stdout, th) | |
| 821 | 821 | if err != nil || !allowed { | |
| 822 | 822 | return allowed, err | |
| 823 | 823 | } | |
| 824 | - | return acceptZotfileConsent(zf, perms) | |
| 824 | + | return acceptZutfileConsent(zf, perms) | |
| 825 | 825 | } | |
| 826 | 826 | ||
| 827 | - | // acceptZotfileConsent records durable consent when the bash mode is not | |
| 827 | + | // acceptZutfileConsent records durable consent when the bash mode is not | |
| 828 | 828 | // "ask". Ask mode never caches receipts so every launch re-prompts unless | |
| 829 | - | // -y / ZOT_AGENT_CONSENT is used for that run. | |
| 830 | - | func acceptZotfileConsent(zf zotfileLoaded, perms tools.PermissionSet) (bool, error) { | |
| 829 | + | // -y / ZUT_AGENT_CONSENT is used for that run. | |
| 830 | + | func acceptZutfileConsent(zf zutfileLoaded, perms tools.PermissionSet) (bool, error) { | |
| 831 | 831 | if strings.ToLower(strings.TrimSpace(perms.Bash.Mode)) == "ask" { | |
| 832 | 832 | return true, nil | |
| 833 | 833 | } | |
| 834 | - | consentPath := filepath.Join(ZotHome(), "agents", safeAgentName(zf.Manifest.Name), "consents", zf.Digest+".json") | |
| 834 | + | consentPath := filepath.Join(ZutHome(), "agents", safeAgentName(zf.Manifest.Name), "consents", zf.Digest+".json") | |
| 835 | 835 | if err := os.MkdirAll(filepath.Dir(consentPath), 0o700); err != nil { | |
| 836 | 836 | return false, fmt.Errorf("save agent consent: %w", err) | |
| 837 | 837 | } | |
| @@ -843,9 +843,9 @@ func acceptZotfileConsent(zf zotfileLoaded, perms tools.PermissionSet) (bool, er | |||
| 843 | 843 | return true, nil | |
| 844 | 844 | } | |
| 845 | 845 | ||
| 846 | - | var errZotfileConsentInterrupted = errors.New("interrupted") | |
| 846 | + | var errZutfileConsentInterrupted = errors.New("interrupted") | |
| 847 | 847 | ||
| 848 | - | func formatZotfileConsent(zf zotfileLoaded, perms tools.PermissionSet, th tui.Theme, color bool) string { | |
| 848 | + | func formatZutfileConsent(zf zutfileLoaded, perms tools.PermissionSet, th tui.Theme, color bool) string { | |
| 849 | 849 | style := func(c tui.TerminalColor, text string) string { | |
| 850 | 850 | if !color { | |
| 851 | 851 | return text | |
| @@ -870,10 +870,10 @@ func formatZotfileConsent(zf zotfileLoaded, perms tools.PermissionSet, th tui.Th | |||
| 870 | 870 | return sb.String() | |
| 871 | 871 | } | |
| 872 | 872 | ||
| 873 | - | // readZotfileConsent accepts one y/n answer followed by Enter without echoing | |
| 873 | + | // readZutfileConsent accepts one y/n answer followed by Enter without echoing | |
| 874 | 874 | // unrelated input. Raw mode also turns Ctrl+C into a byte so the terminal can | |
| 875 | 875 | // be restored before the command aborts. | |
| 876 | - | func readZotfileConsent(in *os.File, out io.Writer, th tui.Theme) (bool, error) { | |
| 876 | + | func readZutfileConsent(in *os.File, out io.Writer, th tui.Theme) (bool, error) { | |
| 877 | 877 | fd := int(in.Fd()) | |
| 878 | 878 | state, err := term.MakeRaw(fd) | |
| 879 | 879 | if err != nil { | |
| @@ -881,7 +881,7 @@ func readZotfileConsent(in *os.File, out io.Writer, th tui.Theme) (bool, error) | |||
| 881 | 881 | } | |
| 882 | 882 | defer func() { _ = term.Restore(fd, state) }() | |
| 883 | 883 | ||
| 884 | - | answer, err := readZotfileConsentKey(in, func(answer byte) { | |
| 884 | + | answer, err := readZutfileConsentKey(in, func(answer byte) { | |
| 885 | 885 | if answer == 0 { | |
| 886 | 886 | fmt.Fprint(out, "\b \b") | |
| 887 | 887 | return | |
| @@ -889,7 +889,7 @@ func readZotfileConsent(in *os.File, out io.Writer, th tui.Theme) (bool, error) | |||
| 889 | 889 | fmt.Fprint(out, th.FGColor(th.FG, string(answer))) | |
| 890 | 890 | }) | |
| 891 | 891 | switch { | |
| 892 | - | case errors.Is(err, errZotfileConsentInterrupted): | |
| 892 | + | case errors.Is(err, errZutfileConsentInterrupted): | |
| 893 | 893 | fmt.Fprint(out, "^C\r\n") | |
| 894 | 894 | return false, err | |
| 895 | 895 | case err != nil: | |
| @@ -901,7 +901,7 @@ func readZotfileConsent(in *os.File, out io.Writer, th tui.Theme) (bool, error) | |||
| 901 | 901 | } | |
| 902 | 902 | } | |
| 903 | 903 | ||
| 904 | - | func readZotfileConsentKey(r io.Reader, echo func(byte)) (byte, error) { | |
| 904 | + | func readZutfileConsentKey(r io.Reader, echo func(byte)) (byte, error) { | |
| 905 | 905 | var answer byte | |
| 906 | 906 | var b [1]byte | |
| 907 | 907 | for { | |
| @@ -928,7 +928,7 @@ func readZotfileConsentKey(r io.Reader, echo func(byte)) (byte, error) { | |||
| 928 | 928 | } | |
| 929 | 929 | } | |
| 930 | 930 | case 3: // Ctrl+C in raw mode. | |
| 931 | - | return 0, errZotfileConsentInterrupted | |
| 931 | + | return 0, errZutfileConsentInterrupted | |
| 932 | 932 | } | |
| 933 | 933 | } | |
| 934 | 934 | } | |
R packages/agent/zotfile_pre_test.go → packages/agent/zutfile_pre_test.go +8 -8 renamed, 72% similar
| @@ -7,8 +7,8 @@ import ( | |||
| 7 | 7 | "strings" | |
| 8 | 8 | "testing" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/core" | |
| 11 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/core" | |
| 11 | + | "github.com/bnema/zut/packages/provider" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | func TestStreamTextSinkDoesNotRepeatCompletedMessage(t *testing.T) { | |
| @@ -26,12 +26,12 @@ func TestStreamTextSinkDoesNotRepeatCompletedMessage(t *testing.T) { | |||
| 26 | 26 | } | |
| 27 | 27 | } | |
| 28 | 28 | ||
| 29 | - | func TestRunZotfileStartupPreShellFailure(t *testing.T) { | |
| 29 | + | func TestRunZutfileStartupPreShellFailure(t *testing.T) { | |
| 30 | 30 | pre := "!false" | |
| 31 | 31 | if runtime.GOOS == "windows" { | |
| 32 | 32 | pre = "!exit 1" | |
| 33 | 33 | } | |
| 34 | - | err := runZotfileStartupPre(context.Background(), pre, t.TempDir(), nil, nil, nil, nil) | |
| 34 | + | err := runZutfileStartupPre(context.Background(), pre, t.TempDir(), nil, nil, nil, nil) | |
| 35 | 35 | if err == nil { | |
| 36 | 36 | t.Fatal("expected entry.pre shell failure") | |
| 37 | 37 | } | |
| @@ -40,19 +40,19 @@ func TestRunZotfileStartupPreShellFailure(t *testing.T) { | |||
| 40 | 40 | } | |
| 41 | 41 | } | |
| 42 | 42 | ||
| 43 | - | func TestRunZotfileStartupPreEmpty(t *testing.T) { | |
| 44 | - | if err := runZotfileStartupPre(context.Background(), " ", t.TempDir(), nil, nil, nil, nil); err != nil { | |
| 43 | + | func TestRunZutfileStartupPreEmpty(t *testing.T) { | |
| 44 | + | if err := runZutfileStartupPre(context.Background(), " ", t.TempDir(), nil, nil, nil, nil); err != nil { | |
| 45 | 45 | t.Fatalf("empty pre: %v", err) | |
| 46 | 46 | } | |
| 47 | 47 | } | |
| 48 | 48 | ||
| 49 | - | func TestRunZotfileStartupPreShellStreams(t *testing.T) { | |
| 49 | + | func TestRunZutfileStartupPreShellStreams(t *testing.T) { | |
| 50 | 50 | cmd := "!printf streamed-pre" | |
| 51 | 51 | if runtime.GOOS == "windows" { | |
| 52 | 52 | cmd = "!echo streamed-pre" | |
| 53 | 53 | } | |
| 54 | 54 | var out strings.Builder | |
| 55 | - | if err := runZotfileStartupPre(context.Background(), cmd, t.TempDir(), nil, nil, nil, &out); err != nil { | |
| 55 | + | if err := runZutfileStartupPre(context.Background(), cmd, t.TempDir(), nil, nil, nil, &out); err != nil { | |
| 56 | 56 | t.Fatal(err) | |
| 57 | 57 | } | |
| 58 | 58 | if !strings.Contains(out.String(), "streamed-pre") { | |
R packages/agent/zotfile_test.go → packages/agent/zutfile_test.go +69 -69 renamed, 71% similar
| @@ -14,11 +14,11 @@ import ( | |||
| 14 | 14 | "strings" | |
| 15 | 15 | "testing" | |
| 16 | 16 | ||
| 17 | - | "github.com/patriceckhart/zot/packages/agent/tools" | |
| 18 | - | "github.com/patriceckhart/zot/packages/tui" | |
| 17 | + | "github.com/bnema/zut/packages/agent/tools" | |
| 18 | + | "github.com/bnema/zut/packages/tui" | |
| 19 | 19 | ) | |
| 20 | 20 | ||
| 21 | - | func TestReadZotfileConsentKeyAcceptsOnlyYesNo(t *testing.T) { | |
| 21 | + | func TestReadZutfileConsentKeyAcceptsOnlyYesNo(t *testing.T) { | |
| 22 | 22 | tests := []struct { | |
| 23 | 23 | name string | |
| 24 | 24 | input []byte | |
| @@ -37,7 +37,7 @@ func TestReadZotfileConsentKeyAcceptsOnlyYesNo(t *testing.T) { | |||
| 37 | 37 | for _, tt := range tests { | |
| 38 | 38 | t.Run(tt.name, func(t *testing.T) { | |
| 39 | 39 | var out bytes.Buffer | |
| 40 | - | got, err := readZotfileConsentKey(bytes.NewReader(tt.input), func(answer byte) { | |
| 40 | + | got, err := readZutfileConsentKey(bytes.NewReader(tt.input), func(answer byte) { | |
| 41 | 41 | if answer == 0 { | |
| 42 | 42 | out.Truncate(out.Len() - 1) | |
| 43 | 43 | return | |
| @@ -57,9 +57,9 @@ func TestReadZotfileConsentKeyAcceptsOnlyYesNo(t *testing.T) { | |||
| 57 | 57 | } | |
| 58 | 58 | } | |
| 59 | 59 | ||
| 60 | - | func TestReadZotfileConsentKeyWaitsForEnter(t *testing.T) { | |
| 60 | + | func TestReadZutfileConsentKeyWaitsForEnter(t *testing.T) { | |
| 61 | 61 | var out bytes.Buffer | |
| 62 | - | if _, err := readZotfileConsentKey(strings.NewReader("y"), func(answer byte) { | |
| 62 | + | if _, err := readZutfileConsentKey(strings.NewReader("y"), func(answer byte) { | |
| 63 | 63 | out.WriteByte(answer) | |
| 64 | 64 | }); !errors.Is(err, io.EOF) { | |
| 65 | 65 | t.Fatalf("error = %v, want EOF before Enter", err) | |
| @@ -69,19 +69,19 @@ func TestReadZotfileConsentKeyWaitsForEnter(t *testing.T) { | |||
| 69 | 69 | } | |
| 70 | 70 | } | |
| 71 | 71 | ||
| 72 | - | func TestReadZotfileConsentKeyInterruptsOnCtrlC(t *testing.T) { | |
| 73 | - | if _, err := readZotfileConsentKey(bytes.NewReader([]byte{'x', 3, 'y'}), nil); !errors.Is(err, errZotfileConsentInterrupted) { | |
| 72 | + | func TestReadZutfileConsentKeyInterruptsOnCtrlC(t *testing.T) { | |
| 73 | + | if _, err := readZutfileConsentKey(bytes.NewReader([]byte{'x', 3, 'y'}), nil); !errors.Is(err, errZutfileConsentInterrupted) { | |
| 74 | 74 | t.Fatalf("error = %v, want interrupted", err) | |
| 75 | 75 | } | |
| 76 | 76 | } | |
| 77 | 77 | ||
| 78 | - | func TestReadZotfileConsentKeyReportsInputError(t *testing.T) { | |
| 79 | - | if _, err := readZotfileConsentKey(strings.NewReader("xxx"), nil); !errors.Is(err, io.EOF) { | |
| 78 | + | func TestReadZutfileConsentKeyReportsInputError(t *testing.T) { | |
| 79 | + | if _, err := readZutfileConsentKey(strings.NewReader("xxx"), nil); !errors.Is(err, io.EOF) { | |
| 80 | 80 | t.Fatalf("error = %v, want EOF", err) | |
| 81 | 81 | } | |
| 82 | 82 | } | |
| 83 | 83 | ||
| 84 | - | func writeTestZotfile(t *testing.T, manifest string) string { | |
| 84 | + | func writeTestZutfile(t *testing.T, manifest string) string { | |
| 85 | 85 | t.Helper() | |
| 86 | 86 | dir := t.TempDir() | |
| 87 | 87 | if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(manifest), 0o600); err != nil { | |
| @@ -93,12 +93,12 @@ func writeTestZotfile(t *testing.T, manifest string) string { | |||
| 93 | 93 | return dir | |
| 94 | 94 | } | |
| 95 | 95 | ||
| 96 | - | func TestLoadZotfileAllowsMissingAgentMD(t *testing.T) { | |
| 96 | + | func TestLoadZutfileAllowsMissingAgentMD(t *testing.T) { | |
| 97 | 97 | dir := t.TempDir() | |
| 98 | - | if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(`{"zotfile":1,"name":"test"}`), 0o600); err != nil { | |
| 98 | + | if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(`{"zutfile":1,"name":"test"}`), 0o600); err != nil { | |
| 99 | 99 | t.Fatal(err) | |
| 100 | 100 | } | |
| 101 | - | zf, cleanup, err := loadZotfile(dir) | |
| 101 | + | zf, cleanup, err := loadZutfile(dir) | |
| 102 | 102 | if cleanup != nil { | |
| 103 | 103 | defer cleanup() | |
| 104 | 104 | } | |
| @@ -110,65 +110,65 @@ func TestLoadZotfileAllowsMissingAgentMD(t *testing.T) { | |||
| 110 | 110 | } | |
| 111 | 111 | } | |
| 112 | 112 | ||
| 113 | - | func TestValidateZotfileDirRejectsAgentMDDirectory(t *testing.T) { | |
| 113 | + | func TestValidateZutfileDirRejectsAgentMDDirectory(t *testing.T) { | |
| 114 | 114 | dir := t.TempDir() | |
| 115 | 115 | if err := os.Mkdir(filepath.Join(dir, "AGENT.md"), 0o700); err != nil { | |
| 116 | 116 | t.Fatal(err) | |
| 117 | 117 | } | |
| 118 | - | if err := validateZotfileDir(dir); err == nil || !strings.Contains(err.Error(), "regular file") { | |
| 118 | + | if err := validateZutfileDir(dir); err == nil || !strings.Contains(err.Error(), "regular file") { | |
| 119 | 119 | t.Fatalf("unexpected error: %v", err) | |
| 120 | 120 | } | |
| 121 | 121 | } | |
| 122 | 122 | ||
| 123 | - | func TestLoadZotfileRejectsUnenforcedPermissions(t *testing.T) { | |
| 123 | + | func TestLoadZutfileRejectsUnenforcedPermissions(t *testing.T) { | |
| 124 | 124 | for _, field := range []string{ | |
| 125 | 125 | `"net":{"allow":["example.com"]}`, | |
| 126 | 126 | `"env":{"read":["HOME"]}`, | |
| 127 | 127 | } { | |
| 128 | - | dir := writeTestZotfile(t, `{"zotfile":1,"name":"test","permissions":{`+field+`}}`) | |
| 129 | - | if _, _, err := loadZotfile(dir); err == nil { | |
| 128 | + | dir := writeTestZutfile(t, `{"zutfile":1,"name":"test","permissions":{`+field+`}}`) | |
| 129 | + | if _, _, err := loadZutfile(dir); err == nil { | |
| 130 | 130 | t.Fatalf("manifest with %s was accepted", field) | |
| 131 | 131 | } | |
| 132 | 132 | } | |
| 133 | 133 | } | |
| 134 | 134 | ||
| 135 | - | func TestLoadZotfileBashModes(t *testing.T) { | |
| 135 | + | func TestLoadZutfileBashModes(t *testing.T) { | |
| 136 | 136 | for _, mode := range []string{"none", "ask", "allowlist"} { | |
| 137 | - | manifest := `{"zotfile":1,"name":"test","permissions":{"bash":{"mode":"` + mode + `"}}}` | |
| 137 | + | manifest := `{"zutfile":1,"name":"test","permissions":{"bash":{"mode":"` + mode + `"}}}` | |
| 138 | 138 | if mode == "allowlist" { | |
| 139 | - | manifest = `{"zotfile":1,"name":"test","permissions":{"bash":{"mode":"allowlist","allow":["git"]}}}` | |
| 139 | + | manifest = `{"zutfile":1,"name":"test","permissions":{"bash":{"mode":"allowlist","allow":["git"]}}}` | |
| 140 | 140 | } | |
| 141 | - | dir := writeTestZotfile(t, manifest) | |
| 142 | - | if _, _, err := loadZotfile(dir); err != nil { | |
| 141 | + | dir := writeTestZutfile(t, manifest) | |
| 142 | + | if _, _, err := loadZutfile(dir); err != nil { | |
| 143 | 143 | t.Fatalf("mode %q rejected: %v", mode, err) | |
| 144 | 144 | } | |
| 145 | 145 | } | |
| 146 | - | dir := writeTestZotfile(t, `{"zotfile":1,"name":"test","permissions":{"bash":{"mode":"all"}}}`) | |
| 147 | - | if _, _, err := loadZotfile(dir); err == nil || !strings.Contains(err.Error(), "unsupported bash permission mode") { | |
| 146 | + | dir := writeTestZutfile(t, `{"zutfile":1,"name":"test","permissions":{"bash":{"mode":"all"}}}`) | |
| 147 | + | if _, _, err := loadZutfile(dir); err == nil || !strings.Contains(err.Error(), "unsupported bash permission mode") { | |
| 148 | 148 | t.Fatalf("unexpected error for all mode: %v", err) | |
| 149 | 149 | } | |
| 150 | - | dir = writeTestZotfile(t, `{"zotfile":1,"name":"test","permissions":{"bash":{"mode":"everything"}}}`) | |
| 151 | - | if _, _, err := loadZotfile(dir); err == nil || !strings.Contains(err.Error(), "unsupported bash permission mode") { | |
| 150 | + | dir = writeTestZutfile(t, `{"zutfile":1,"name":"test","permissions":{"bash":{"mode":"everything"}}}`) | |
| 151 | + | if _, _, err := loadZutfile(dir); err == nil || !strings.Contains(err.Error(), "unsupported bash permission mode") { | |
| 152 | 152 | t.Fatalf("unexpected error for unknown mode: %v", err) | |
| 153 | 153 | } | |
| 154 | 154 | } | |
| 155 | 155 | ||
| 156 | - | func TestLoadZotfileRejectsUnsafeOrCollidingNames(t *testing.T) { | |
| 156 | + | func TestLoadZutfileRejectsUnsafeOrCollidingNames(t *testing.T) { | |
| 157 | 157 | for _, name := range []string{"...", "Name", "two words", "a/b"} { | |
| 158 | - | dir := writeTestZotfile(t, `{"zotfile":1,"name":"`+name+`"}`) | |
| 159 | - | if _, _, err := loadZotfile(dir); err == nil { | |
| 158 | + | dir := writeTestZutfile(t, `{"zutfile":1,"name":"`+name+`"}`) | |
| 159 | + | if _, _, err := loadZutfile(dir); err == nil { | |
| 160 | 160 | t.Fatalf("unsafe manifest name %q was accepted", name) | |
| 161 | 161 | } | |
| 162 | 162 | } | |
| 163 | 163 | } | |
| 164 | 164 | ||
| 165 | - | func TestResolveZotfileRefUsesLocalFirstThenGitHubShorthand(t *testing.T) { | |
| 165 | + | func TestResolveZutfileRefUsesLocalFirstThenGitHubShorthand(t *testing.T) { | |
| 166 | 166 | dir := t.TempDir() | |
| 167 | 167 | t.Chdir(dir) | |
| 168 | 168 | if err := os.Mkdir("local-agent", 0o700); err != nil { | |
| 169 | 169 | t.Fatal(err) | |
| 170 | 170 | } | |
| 171 | - | if err := os.WriteFile("packed-agent.zot", []byte("test"), 0o600); err != nil { | |
| 171 | + | if err := os.WriteFile("packed-agent.zut", []byte("test"), 0o600); err != nil { | |
| 172 | 172 | t.Fatal(err) | |
| 173 | 173 | } | |
| 174 | 174 | ||
| @@ -177,9 +177,9 @@ func TestResolveZotfileRefUsesLocalFirstThenGitHubShorthand(t *testing.T) { | |||
| 177 | 177 | want string | |
| 178 | 178 | }{ | |
| 179 | 179 | {"local-agent", "local-agent"}, | |
| 180 | - | {"packed-agent", "packed-agent.zot"}, | |
| 180 | + | {"packed-agent", "packed-agent.zut"}, | |
| 181 | 181 | {"remote-agent", "remote-agent"}, | |
| 182 | - | {"missing.zot", "missing.zot"}, | |
| 182 | + | {"missing.zut", "missing.zut"}, | |
| 183 | 183 | {"agents/remote-agent", "https://github.com/agents/remote-agent"}, | |
| 184 | 184 | {`agents\remote-agent`, "https://github.com/agents/remote-agent"}, | |
| 185 | 185 | {"frkr/zot-archify", "https://github.com/frkr/zot-archify"}, | |
| @@ -190,7 +190,7 @@ func TestResolveZotfileRefUsesLocalFirstThenGitHubShorthand(t *testing.T) { | |||
| 190 | 190 | {"https://github.com/acme/agents/example", "https://github.com/acme/agents/example"}, | |
| 191 | 191 | } | |
| 192 | 192 | for _, tt := range tests { | |
| 193 | - | got, err := resolveZotfileRef(tt.ref) | |
| 193 | + | got, err := resolveZutfileRef(tt.ref) | |
| 194 | 194 | if err != nil { | |
| 195 | 195 | t.Fatalf("resolve %q: %v", tt.ref, err) | |
| 196 | 196 | } | |
| @@ -200,8 +200,8 @@ func TestResolveZotfileRefUsesLocalFirstThenGitHubShorthand(t *testing.T) { | |||
| 200 | 200 | } | |
| 201 | 201 | } | |
| 202 | 202 | ||
| 203 | - | func TestLoadZotfileRejectsBundledExecutableExtension(t *testing.T) { | |
| 204 | - | dir := writeTestZotfile(t, `{"zotfile":1,"name":"test"}`) | |
| 203 | + | func TestLoadZutfileRejectsBundledExecutableExtension(t *testing.T) { | |
| 204 | + | dir := writeTestZutfile(t, `{"zutfile":1,"name":"test"}`) | |
| 205 | 205 | ext := filepath.Join(dir, "extensions", "bad") | |
| 206 | 206 | if err := os.MkdirAll(ext, 0o700); err != nil { | |
| 207 | 207 | t.Fatal(err) | |
| @@ -209,31 +209,31 @@ func TestLoadZotfileRejectsBundledExecutableExtension(t *testing.T) { | |||
| 209 | 209 | if err := os.WriteFile(filepath.Join(ext, "extension.json"), []byte(`{"name":"bad"}`), 0o600); err != nil { | |
| 210 | 210 | t.Fatal(err) | |
| 211 | 211 | } | |
| 212 | - | if _, _, err := loadZotfile(dir); err == nil || !strings.Contains(err.Error(), "cannot yet be confined") { | |
| 212 | + | if _, _, err := loadZutfile(dir); err == nil || !strings.Contains(err.Error(), "cannot yet be confined") { | |
| 213 | 213 | t.Fatalf("unexpected error: %v", err) | |
| 214 | 214 | } | |
| 215 | 215 | } | |
| 216 | 216 | ||
| 217 | - | func TestCheckZotfileMinVersion(t *testing.T) { | |
| 218 | - | var zf zotfileLoaded | |
| 219 | - | zf.Manifest.Runtime.MinZot = "0.3.0" | |
| 220 | - | if err := checkZotfileRequirements(zf, "0.2.75"); err == nil { | |
| 221 | - | t.Fatal("old zot version accepted") | |
| 217 | + | func TestCheckZutfileMinVersion(t *testing.T) { | |
| 218 | + | var zf zutfileLoaded | |
| 219 | + | zf.Manifest.Runtime.MinZut = "0.3.0" | |
| 220 | + | if err := checkZutfileRequirements(zf, "0.2.75"); err == nil { | |
| 221 | + | t.Fatal("old zut version accepted") | |
| 222 | 222 | } | |
| 223 | - | if err := checkZotfileRequirements(zf, "0.3.0"); err != nil { | |
| 223 | + | if err := checkZutfileRequirements(zf, "0.3.0"); err != nil { | |
| 224 | 224 | t.Fatalf("minimum version rejected: %v", err) | |
| 225 | 225 | } | |
| 226 | 226 | } | |
| 227 | 227 | ||
| 228 | - | func TestApplyZotfileModelRequirementsRejectsUnsupportedFields(t *testing.T) { | |
| 229 | - | var m ZotfileManifest | |
| 228 | + | func TestApplyZutfileModelRequirementsRejectsUnsupportedFields(t *testing.T) { | |
| 229 | + | var m ZutfileManifest | |
| 230 | 230 | m.Model.MinTier = "frontier" | |
| 231 | - | if err := applyZotfileModelRequirements(&Args{}, m); err == nil { | |
| 231 | + | if err := applyZutfileModelRequirements(&Args{}, m); err == nil { | |
| 232 | 232 | t.Fatal("unsupported min_tier was ignored") | |
| 233 | 233 | } | |
| 234 | 234 | m.Model.MinTier = "" | |
| 235 | 235 | m.Model.Requires = []string{"audio"} | |
| 236 | - | if err := applyZotfileModelRequirements(&Args{}, m); err == nil { | |
| 236 | + | if err := applyZutfileModelRequirements(&Args{}, m); err == nil { | |
| 237 | 237 | t.Fatal("unsupported capability was ignored") | |
| 238 | 238 | } | |
| 239 | 239 | } | |
| @@ -251,7 +251,7 @@ func TestUntarRejectsTraversalAndOversizedEntry(t *testing.T) { | |||
| 251 | 251 | if err := untar(bytes.NewReader(makeTar("../escape", 0)), t.TempDir()); err == nil { | |
| 252 | 252 | t.Fatal("path traversal accepted") | |
| 253 | 253 | } | |
| 254 | - | if err := untar(bytes.NewReader(makeTar("large", maxZotfileEntrySize+1)), t.TempDir()); err == nil { | |
| 254 | + | if err := untar(bytes.NewReader(makeTar("large", maxZutfileEntrySize+1)), t.TempDir()); err == nil { | |
| 255 | 255 | t.Fatal("oversized entry accepted") | |
| 256 | 256 | } | |
| 257 | 257 | } | |
| @@ -281,13 +281,13 @@ func TestParseGitHubAgentURL(t *testing.T) { | |||
| 281 | 281 | } | |
| 282 | 282 | } | |
| 283 | 283 | ||
| 284 | - | func TestLoadRemoteZotfileDownloadsTemporaryGitHubArchive(t *testing.T) { | |
| 284 | + | func TestLoadRemoteZutfileDownloadsTemporaryGitHubArchive(t *testing.T) { | |
| 285 | 285 | var archive bytes.Buffer | |
| 286 | 286 | gz := gzip.NewWriter(&archive) | |
| 287 | 287 | tw := tar.NewWriter(gz) | |
| 288 | 288 | files := map[string]string{ | |
| 289 | - | "agents-main/zot-maintenance/manifest.json": `{"zotfile":1,"name":"zot-maintenance"}`, | |
| 290 | - | "agents-main/zot-maintenance/AGENT.md": "Maintain zot.", | |
| 289 | + | "agents-main/zut-maintenance/manifest.json": `{"zutfile":1,"name":"zut-maintenance"}`, | |
| 290 | + | "agents-main/zut-maintenance/AGENT.md": "Maintain zut.", | |
| 291 | 291 | } | |
| 292 | 292 | for name, content := range files { | |
| 293 | 293 | if err := tw.WriteHeader(&tar.Header{Name: name, Mode: 0o600, Size: int64(len(content))}); err != nil { | |
| @@ -313,13 +313,13 @@ func TestLoadRemoteZotfileDownloadsTemporaryGitHubArchive(t *testing.T) { | |||
| 313 | 313 | githubArchiveURL = func(_, _, _ string) string { return server.URL } | |
| 314 | 314 | t.Cleanup(func() { githubArchiveURL = oldArchiveURL }) | |
| 315 | 315 | ||
| 316 | - | u, _ := url.Parse("https://github.com/acme/agents/zot-maintenance") | |
| 317 | - | zf, cleanup, err := loadRemoteZotfile(u) | |
| 316 | + | u, _ := url.Parse("https://github.com/acme/agents/zut-maintenance") | |
| 317 | + | zf, cleanup, err := loadRemoteZutfile(u) | |
| 318 | 318 | if err != nil { | |
| 319 | 319 | t.Fatal(err) | |
| 320 | 320 | } | |
| 321 | - | if zf.Manifest.Name != "zot-maintenance" || !zf.Temp { | |
| 322 | - | t.Fatalf("unexpected zotfile: %+v", zf) | |
| 321 | + | if zf.Manifest.Name != "zut-maintenance" || !zf.Temp { | |
| 322 | + | t.Fatalf("unexpected zutfile: %+v", zf) | |
| 323 | 323 | } | |
| 324 | 324 | root := filepath.Dir(filepath.Dir(zf.Dir)) | |
| 325 | 325 | cleanup() | |
| @@ -328,19 +328,19 @@ func TestLoadRemoteZotfileDownloadsTemporaryGitHubArchive(t *testing.T) { | |||
| 328 | 328 | } | |
| 329 | 329 | } | |
| 330 | 330 | ||
| 331 | - | func TestFormatZotfileConsentUsesThemeColors(t *testing.T) { | |
| 331 | + | func TestFormatZutfileConsentUsesThemeColors(t *testing.T) { | |
| 332 | 332 | th := tui.Theme{ | |
| 333 | 333 | FG: tui.Color256(1), Muted: tui.Color256(2), Accent: tui.Color256(3), | |
| 334 | 334 | Assistant: tui.Color256(4), Warning: tui.Color256(5), | |
| 335 | 335 | } | |
| 336 | - | zf := zotfileLoaded{} | |
| 336 | + | zf := zutfileLoaded{} | |
| 337 | 337 | zf.Manifest.Name = "reviewer" | |
| 338 | 338 | zf.Manifest.Version = "1.2.3" | |
| 339 | 339 | perms := tools.PermissionSet{} | |
| 340 | 340 | perms.FS.Read = []string{"/workspace"} | |
| 341 | 341 | perms.Bash.Mode = "ask" | |
| 342 | 342 | ||
| 343 | - | got := formatZotfileConsent(zf, perms, th, true) | |
| 343 | + | got := formatZutfileConsent(zf, perms, th, true) | |
| 344 | 344 | for _, want := range []string{ | |
| 345 | 345 | th.FGColor(th.Assistant, "Agent"), | |
| 346 | 346 | th.FGColor(th.Accent, "reviewer@1.2.3"), | |
| @@ -377,13 +377,13 @@ func TestPeelLeadingYes(t *testing.T) { | |||
| 377 | 377 | } | |
| 378 | 378 | } | |
| 379 | 379 | ||
| 380 | - | func TestConsentZotfileAutoYesWritesReceipt(t *testing.T) { | |
| 380 | + | func TestConsentZutfileAutoYesWritesReceipt(t *testing.T) { | |
| 381 | 381 | home := t.TempDir() | |
| 382 | - | t.Setenv("ZOT_HOME", home) | |
| 383 | - | zf := zotfileLoaded{Digest: "abc123", Manifest: ZotfileManifest{Name: "demo", Version: "1.0.0"}} | |
| 382 | + | t.Setenv("ZUT_HOME", home) | |
| 383 | + | zf := zutfileLoaded{Digest: "abc123", Manifest: ZutfileManifest{Name: "demo", Version: "1.0.0"}} | |
| 384 | 384 | var perms tools.PermissionSet | |
| 385 | 385 | perms.Bash.Mode = "none" | |
| 386 | - | allowed, err := consentZotfile(zf, perms, true) | |
| 386 | + | allowed, err := consentZutfile(zf, perms, true) | |
| 387 | 387 | if err != nil || !allowed { | |
| 388 | 388 | t.Fatalf("auto yes: allowed=%v err=%v", allowed, err) | |
| 389 | 389 | } | |
| @@ -392,19 +392,19 @@ func TestConsentZotfileAutoYesWritesReceipt(t *testing.T) { | |||
| 392 | 392 | t.Fatalf("consent receipt missing: %v", err) | |
| 393 | 393 | } | |
| 394 | 394 | // Second call without -y should use the receipt. | |
| 395 | - | allowed, err = consentZotfile(zf, perms, false) | |
| 395 | + | allowed, err = consentZutfile(zf, perms, false) | |
| 396 | 396 | if err != nil || !allowed { | |
| 397 | 397 | t.Fatalf("cached consent: allowed=%v err=%v", allowed, err) | |
| 398 | 398 | } | |
| 399 | 399 | } | |
| 400 | 400 | ||
| 401 | - | func TestConsentZotfileAutoYesAskDoesNotCache(t *testing.T) { | |
| 401 | + | func TestConsentZutfileAutoYesAskDoesNotCache(t *testing.T) { | |
| 402 | 402 | home := t.TempDir() | |
| 403 | - | t.Setenv("ZOT_HOME", home) | |
| 404 | - | zf := zotfileLoaded{Digest: "askdig", Manifest: ZotfileManifest{Name: "ask-agent", Version: "1.0.0"}} | |
| 403 | + | t.Setenv("ZUT_HOME", home) | |
| 404 | + | zf := zutfileLoaded{Digest: "askdig", Manifest: ZutfileManifest{Name: "ask-agent", Version: "1.0.0"}} | |
| 405 | 405 | var perms tools.PermissionSet | |
| 406 | 406 | perms.Bash.Mode = "ask" | |
| 407 | - | if _, err := consentZotfile(zf, perms, true); err != nil { | |
| 407 | + | if _, err := consentZutfile(zf, perms, true); err != nil { | |
| 408 | 408 | t.Fatal(err) | |
| 409 | 409 | } | |
| 410 | 410 | path := filepath.Join(home, "agents", "ask-agent", "consents", "askdig.json") | |
M packages/core/agent.go +1 -1
| @@ -10,7 +10,7 @@ import ( | |||
| 10 | 10 | "time" | |
| 11 | 11 | "unicode/utf8" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | // Agent is a stateful conversation bound to a provider client, a model, | |
M packages/core/agent_context_test.go +1 -1
| @@ -3,7 +3,7 @@ package core | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 6 | + | "github.com/bnema/zut/packages/provider" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestAppendUserContextAddsAndPublishesMessage(t *testing.T) { | |
M packages/core/agent_retry_test.go +1 -1
| @@ -10,7 +10,7 @@ import ( | |||
| 10 | 10 | "time" | |
| 11 | 11 | "unicode/utf8" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | type retryFakeClient struct { | |
M packages/core/compact.go +1 -1
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "strings" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // Compact summarizes the agent's transcript via the LLM and replaces | |
M packages/core/core_test.go +2 -2
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "testing" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func TestAgentRejectsEmptyPrompt(t *testing.T) { | |
| @@ -42,7 +42,7 @@ func TestAgentSetPromptConfigSwapsPromptAndToolsAtomically(t *testing.T) { | |||
| 42 | 42 | ||
| 43 | 43 | func TestSessionRoundTrip(t *testing.T) { | |
| 44 | 44 | dir := t.TempDir() | |
| 45 | - | os.Setenv("ZOT_HOME", dir) | |
| 45 | + | os.Setenv("ZUT_HOME", dir) | |
| 46 | 46 | ||
| 47 | 47 | sess, err := NewSession(dir, "/tmp/project", "anthropic", "claude-sonnet-4-5", "test") | |
| 48 | 48 | if err != nil { | |
M packages/core/cost.go +1 -1
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | package core | |
| 2 | 2 | ||
| 3 | - | import "github.com/patriceckhart/zot/packages/provider" | |
| 3 | + | import "github.com/bnema/zut/packages/provider" | |
| 4 | 4 | ||
| 5 | 5 | // CostTracker accumulates usage across turns in a session. | |
| 6 | 6 | // | |
M packages/core/events.go +1 -1
| @@ -3,7 +3,7 @@ package core | |||
| 3 | 3 | import ( | |
| 4 | 4 | "encoding/json" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 6 | + | "github.com/bnema/zut/packages/provider" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | // AgentEvent is the superset of events emitted by an Agent run. | |
M packages/core/fast_mode_test.go +1 -1
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 8 | + | "github.com/bnema/zut/packages/provider" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | type fastModeTestClient struct { | |
M packages/core/intercept_test.go +1 -1
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | "encoding/json" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 8 | + | "github.com/bnema/zut/packages/provider" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // recordingTool captures the args it was invoked with so the test | |
M packages/core/queue_test.go +1 -1
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "sync/atomic" | |
| 8 | 8 | "testing" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | type queueFakeClient struct { | |
M packages/core/session.go +4 -4
| @@ -16,8 +16,8 @@ import ( | |||
| 16 | 16 | "strings" | |
| 17 | 17 | "time" | |
| 18 | 18 | ||
| 19 | + | "github.com/bnema/zut/packages/provider" | |
| 19 | 20 | "github.com/google/uuid" | |
| 20 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 21 | 21 | ) | |
| 22 | 22 | ||
| 23 | 23 | // Session is a JSONL-backed conversation transcript tied to a cwd. | |
| @@ -782,7 +782,7 @@ func OpenSession(path string) (*Session, []provider.Message, error) { | |||
| 782 | 782 | // | |
| 783 | 783 | // Corruption gets into the transcript two ways we know of: | |
| 784 | 784 | // | |
| 785 | - | // - Older zot builds that persisted the assistant tool_use row | |
| 785 | + | // - Older zut builds that persisted the assistant tool_use row | |
| 786 | 786 | // before the tool_result row, then crashed between the two. | |
| 787 | 787 | // - Abort paths in older builds that didn't drop the mid-turn | |
| 788 | 788 | // assistant message cleanly. | |
| @@ -1025,7 +1025,7 @@ func firstTextFromMessage(msg provider.Message) string { | |||
| 1025 | 1025 | ||
| 1026 | 1026 | // PruneEmptySessions deletes session files in cwd's session directory | |
| 1027 | 1027 | // that contain only a meta line (no messages were ever appended). | |
| 1028 | - | // Cleans up the backlog of empty stubs created by old zot versions | |
| 1028 | + | // Cleans up the backlog of empty stubs created by old zut versions | |
| 1029 | 1029 | // that wrote a meta line at NewSession time and never followed up. | |
| 1030 | 1030 | // Errors are swallowed; the caller treats this as best-effort. | |
| 1031 | 1031 | func PruneEmptySessions(root, cwd string) { | |
| @@ -1255,7 +1255,7 @@ func (s *Session) AppendExtensionState(extension string, state json.RawMessage) | |||
| 1255 | 1255 | ||
| 1256 | 1256 | // Close flushes and closes the session file. If the session was | |
| 1257 | 1257 | // freshly created in this process and never had any messages | |
| 1258 | - | // appended (the user opened zot, looked around, and exited without | |
| 1258 | + | // appended (the user opened zut, looked around, and exited without | |
| 1259 | 1259 | // prompting), the file is deleted on close so the sessions list | |
| 1260 | 1260 | // doesn't fill up with empty meta-only stubs. | |
| 1261 | 1261 | func (s *Session) Close() error { | |
M packages/core/session_deferred_tools_test.go +1 -1
| @@ -3,7 +3,7 @@ package core | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 6 | + | "github.com/bnema/zut/packages/provider" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestSessionPreservesActivatedDeferredTools(t *testing.T) { | |
M packages/core/session_history_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "testing" | |
| 5 | 5 | "time" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 7 | + | "github.com/bnema/zut/packages/provider" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestReadSessionHistoryRetainsPreCompactionSegments(t *testing.T) { | |
M packages/core/session_portable.go +10 -10
| @@ -13,19 +13,19 @@ import ( | |||
| 13 | 13 | "strings" | |
| 14 | 14 | "time" | |
| 15 | 15 | ||
| 16 | + | "github.com/bnema/zut/packages/provider" | |
| 16 | 17 | "github.com/google/uuid" | |
| 17 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 18 | 18 | ) | |
| 19 | 19 | ||
| 20 | 20 | // PortableExt is the filesystem extension used for exported sessions. | |
| 21 | - | // A ".zotsession" is just a zot JSONL session file with the meta | |
| 21 | + | // A ".zutsession" is just a zut JSONL session file with the meta | |
| 22 | 22 | // header rewritten so the importing user gets fresh ownership. | |
| 23 | - | const PortableExt = ".zotsession" | |
| 23 | + | const PortableExt = ".zutsession" | |
| 24 | 24 | ||
| 25 | 25 | // ExportSession writes the session at srcPath to dstPath as a | |
| 26 | - | // portable .zotsession file. If dstPath is an existing directory the | |
| 26 | + | // portable .zutsession file. If dstPath is an existing directory the | |
| 27 | 27 | // file is created inside it with a name derived from the session's | |
| 28 | - | // meta ("YYYYMMDD-HHMMSS-<first-prompt-excerpt>.zotsession"). The | |
| 28 | + | // meta ("YYYYMMDD-HHMMSS-<first-prompt-excerpt>.zutsession"). The | |
| 29 | 29 | // destination's directory is created if needed. Returns the final | |
| 30 | 30 | // resolved path so the caller can tell the user where it landed. | |
| 31 | 31 | // | |
| @@ -72,7 +72,7 @@ func ExportSession(srcPath, dstPath string) (string, error) { | |||
| 72 | 72 | ||
| 73 | 73 | // Scan the rest of the file for the first user message so we can | |
| 74 | 74 | // build a humane filename. Only reads if dstPath doesn't already | |
| 75 | - | // end in .zotsession. | |
| 75 | + | // end in .zutsession. | |
| 76 | 76 | firstPrompt := "" | |
| 77 | 77 | if !strings.HasSuffix(strings.ToLower(dstPath), PortableExt) { | |
| 78 | 78 | if fi, _ := os.Stat(dstPath); fi == nil || fi.IsDir() { | |
| @@ -156,13 +156,13 @@ func ExportSession(srcPath, dstPath string) (string, error) { | |||
| 156 | 156 | return outPath, nil | |
| 157 | 157 | } | |
| 158 | 158 | ||
| 159 | - | // ImportSession copies the .zotsession file at srcPath into the | |
| 159 | + | // ImportSession copies the .zutsession file at srcPath into the | |
| 160 | 160 | // running user's session store under the given root+cwd, rewriting | |
| 161 | 161 | // the meta's id / cwd / started fields so the imported session is | |
| 162 | 162 | // owned by the current user / directory / clock. Returns the path | |
| 163 | 163 | // of the created session file, ready to pass to OpenSession. | |
| 164 | 164 | // | |
| 165 | - | // The imported session is a first-class zot session: it'll show up | |
| 165 | + | // The imported session is a first-class zut session: it'll show up | |
| 166 | 166 | // in /sessions, /jump, and on-disk summaries just like any other. | |
| 167 | 167 | // Messages and usage rows are preserved verbatim. | |
| 168 | 168 | func ImportSession(srcPath, root, cwd, version string) (string, error) { | |
| @@ -434,7 +434,7 @@ func writeBranchSession(root, cwd, version string, parent SessionMeta, messages | |||
| 434 | 434 | // Build in a same-directory temporary file. Rename is atomic only when | |
| 435 | 435 | // the temporary and final paths share a directory; closing the file first | |
| 436 | 436 | // also ensures no buffered writer or descriptor can expose a partial branch. | |
| 437 | - | dst, err := os.CreateTemp(dir, ".zot-branch-*.tmp") | |
| 437 | + | dst, err := os.CreateTemp(dir, ".zut-branch-*.tmp") | |
| 438 | 438 | if err != nil { | |
| 439 | 439 | return "", fmt.Errorf("branch: create temp: %w", err) | |
| 440 | 440 | } | |
| @@ -910,7 +910,7 @@ func firstUserPrompt(src io.Reader) (string, error) { | |||
| 910 | 910 | } | |
| 911 | 911 | } | |
| 912 | 912 | ||
| 913 | - | // filenameFor builds a descriptive .zotsession filename from the | |
| 913 | + | // filenameFor builds a descriptive .zutsession filename from the | |
| 914 | 914 | // session's start time and, when available, an excerpt of the | |
| 915 | 915 | // first user prompt. | |
| 916 | 916 | func filenameFor(started time.Time, id, firstPrompt string) string { | |
M packages/core/session_portable_test.go +4 -4
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "strings" | |
| 8 | 8 | "testing" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | // TestSessionExportImportRoundTrip writes a few messages to a live | |
| @@ -159,7 +159,7 @@ func TestScanSessionMetaReturnsLatestRowWithoutHydratingMessages(t *testing.T) { | |||
| 159 | 159 | } | |
| 160 | 160 | ||
| 161 | 161 | // TestExportToFilePath writes to an explicit file path (no directory | |
| 162 | - | // guessing) and checks the .zotsession extension is appended when missing. | |
| 162 | + | // guessing) and checks the .zutsession extension is appended when missing. | |
| 163 | 163 | func TestExportToFilePath(t *testing.T) { | |
| 164 | 164 | root := t.TempDir() | |
| 165 | 165 | sess, err := NewSession(root, "/cwd", "anthropic", "claude-opus-4-7", "0.0.0-test") | |
| @@ -172,14 +172,14 @@ func TestExportToFilePath(t *testing.T) { | |||
| 172 | 172 | }) | |
| 173 | 173 | _ = sess.Close() | |
| 174 | 174 | ||
| 175 | - | // No extension — should add .zotsession. | |
| 175 | + | // No extension — should add .zutsession. | |
| 176 | 176 | dst := filepath.Join(t.TempDir(), "mysession") | |
| 177 | 177 | out, err := ExportSession(sess.Path, dst) | |
| 178 | 178 | if err != nil { | |
| 179 | 179 | t.Fatal(err) | |
| 180 | 180 | } | |
| 181 | 181 | if !strings.HasSuffix(out, PortableExt) { | |
| 182 | - | t.Errorf("want .zotsession suffix on %q", out) | |
| 182 | + | t.Errorf("want .zutsession suffix on %q", out) | |
| 183 | 183 | } | |
| 184 | 184 | } | |
| 185 | 185 | ||
M packages/core/session_repair_test.go +1 -1
| @@ -3,7 +3,7 @@ package core | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 6 | + | "github.com/bnema/zut/packages/provider" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | // TestRepairToolUseResultPairsAppendsStub covers the most common | |
M packages/core/session_snapshot_test.go +1 -1
| @@ -10,7 +10,7 @@ import ( | |||
| 10 | 10 | "testing" | |
| 11 | 11 | "time" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 13 | + | "github.com/bnema/zut/packages/provider" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | func TestAppendMessageRejectsEmptyContent(t *testing.T) { | |
M packages/core/session_usage_test.go +1 -1
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "testing" | |
| 5 | 5 | "time" | |
| 6 | 6 | ||
| 7 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 7 | + | "github.com/bnema/zut/packages/provider" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestSessionUsageDetailResetsContextAfterCompaction(t *testing.T) { | |
M packages/core/title.go +1 -1
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | "unicode" | |
| 8 | 8 | ||
| 9 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 9 | + | "github.com/bnema/zut/packages/provider" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | // SessionTitleMaxChars is the maximum length of an automatically generated | |
M packages/core/title_test.go +1 -1
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | "testing" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 10 | + | "github.com/bnema/zut/packages/provider" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | type titleTestClient struct { | |
M packages/core/tool.go +1 -1
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | 9 | "fmt" | |
| 10 | 10 | "sort" | |
| 11 | 11 | ||
| 12 | - | "github.com/patriceckhart/zot/packages/provider" | |
| 12 | + | "github.com/bnema/zut/packages/provider" | |
| 13 | 13 | ) | |
| 14 | 14 | ||
| 15 | 15 | // Tool is a capability the agent can invoke. | |
M packages/ignore/gitignore.go +1 -1
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | // Package ignore provides a minimal .gitignore matcher shared across | |
| 2 | - | // zot. It is intentionally small: enough to drop obvious non-source | |
| 2 | + | // zut. It is intentionally small: enough to drop obvious non-source | |
| 3 | 3 | // directories (build outputs, dependency and tool caches) from | |
| 4 | 4 | // recursive walks, not a faithful git reimplementation. | |
| 5 | 5 | package ignore | |
M packages/provider/amazon_bedrock.go +1 -1
| @@ -610,7 +610,7 @@ func (c *bedrockClient) Stream(ctx context.Context, req Request) (<-chan Event, | |||
| 610 | 610 | msg := strings.TrimSpace(string(b)) | |
| 611 | 611 | // A 403 on the bearer route is almost always a region mismatch: | |
| 612 | 612 | // short-term Bedrock API keys are scoped to the region of the | |
| 613 | - | // console session that minted them, but zot defaults to | |
| 613 | + | // console session that minted them, but zut defaults to | |
| 614 | 614 | // us-east-1. Surface the resolved region and the fix so the user | |
| 615 | 615 | // is not left guessing why a freshly-copied key is "invalid". | |
| 616 | 616 | if resp.StatusCode == http.StatusForbidden && c.bearerToken != "" { | |
M packages/provider/anthropic.go +2 -2
| @@ -535,11 +535,11 @@ func (c *anthropicClient) Stream(ctx context.Context, req Request) (<-chan Event | |||
| 535 | 535 | return nil, err | |
| 536 | 536 | } | |
| 537 | 537 | ||
| 538 | - | // Optional debug dump: when $ZOT_DEBUG_ANTHROPIC is a file path | |
| 538 | + | // Optional debug dump: when $ZUT_DEBUG_ANTHROPIC is a file path | |
| 539 | 539 | // we append every outgoing request body to it, one JSON object | |
| 540 | 540 | // per line. Useful for diffing turn N vs turn N+1 to understand | |
| 541 | 541 | // why the cache prefix isn't matching. | |
| 542 | - | if dump := os.Getenv("ZOT_DEBUG_ANTHROPIC"); dump != "" { | |
| 542 | + | if dump := os.Getenv("ZUT_DEBUG_ANTHROPIC"); dump != "" { | |
| 543 | 543 | if f, derr := os.OpenFile(dump, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600); derr == nil { | |
| 544 | 544 | _, _ = f.Write(body) | |
| 545 | 545 | _, _ = f.Write([]byte{'\n'}) | |
M packages/provider/anthropic_image.go +1 -1
| @@ -17,7 +17,7 @@ import ( | |||
| 17 | 17 | ||
| 18 | 18 | // anthMaxImageSide is Anthropic's per-image dimension cap when a | |
| 19 | 19 | // request contains more than one image. Single-image requests are | |
| 20 | - | // allowed up to 8000 px, but zot conversations routinely include | |
| 20 | + | // allowed up to 8000 px, but zut conversations routinely include | |
| 21 | 21 | // multiple images (a screenshot here, a tool result image there) | |
| 22 | 22 | // so we always normalise down to the stricter limit. 2000 px on the | |
| 23 | 23 | // longest side is well below their cap and still readable for the | |
M packages/provider/auth/api_key_command_test.go +9 -9
| @@ -10,7 +10,7 @@ import ( | |||
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | func TestAPIKeyCommandHelperProcess(t *testing.T) { | |
| 13 | - | if os.Getenv("ZOT_API_KEY_COMMAND_HELPER") != "1" { | |
| 13 | + | if os.Getenv("ZUT_API_KEY_COMMAND_HELPER") != "1" { | |
| 14 | 14 | return | |
| 15 | 15 | } | |
| 16 | 16 | args := os.Args | |
| @@ -22,9 +22,9 @@ func TestAPIKeyCommandHelperProcess(t *testing.T) { | |||
| 22 | 22 | } | |
| 23 | 23 | switch args[1] { | |
| 24 | 24 | case "print": | |
| 25 | - | fmt.Print(os.Getenv("ZOT_API_KEY_COMMAND_VALUE")) | |
| 25 | + | fmt.Print(os.Getenv("ZUT_API_KEY_COMMAND_VALUE")) | |
| 26 | 26 | case "fail": | |
| 27 | - | fmt.Fprint(os.Stderr, os.Getenv("ZOT_API_KEY_COMMAND_VALUE")) | |
| 27 | + | fmt.Fprint(os.Stderr, os.Getenv("ZUT_API_KEY_COMMAND_VALUE")) | |
| 28 | 28 | os.Exit(1) | |
| 29 | 29 | case "sleep": | |
| 30 | 30 | time.Sleep(5 * time.Second) | |
| @@ -71,8 +71,8 @@ func resetAPIKeyCommandCache(t *testing.T) { | |||
| 71 | 71 | ||
| 72 | 72 | func TestResolveAPIKeyCommand(t *testing.T) { | |
| 73 | 73 | resetAPIKeyCommandCache(t) | |
| 74 | - | t.Setenv("ZOT_API_KEY_COMMAND_HELPER", "1") | |
| 75 | - | t.Setenv("ZOT_API_KEY_COMMAND_VALUE", "secret-value\r\n") | |
| 74 | + | t.Setenv("ZUT_API_KEY_COMMAND_HELPER", "1") | |
| 75 | + | t.Setenv("ZUT_API_KEY_COMMAND_VALUE", "secret-value\r\n") | |
| 76 | 76 | ||
| 77 | 77 | got, err := ResolveAPIKeyCommand(context.Background(), apiKeyHelperCommand("print")) | |
| 78 | 78 | if err != nil { | |
| @@ -85,7 +85,7 @@ func TestResolveAPIKeyCommand(t *testing.T) { | |||
| 85 | 85 | ||
| 86 | 86 | func TestResolveAPIKeyCommandCachesSuccess(t *testing.T) { | |
| 87 | 87 | resetAPIKeyCommandCache(t) | |
| 88 | - | t.Setenv("ZOT_API_KEY_COMMAND_HELPER", "1") | |
| 88 | + | t.Setenv("ZUT_API_KEY_COMMAND_HELPER", "1") | |
| 89 | 89 | countPath := t.TempDir() + string(os.PathSeparator) + "count" | |
| 90 | 90 | command := apiKeyHelperCommand("count", countPath) | |
| 91 | 91 | ||
| @@ -109,8 +109,8 @@ func TestResolveAPIKeyCommandCachesSuccess(t *testing.T) { | |||
| 109 | 109 | ||
| 110 | 110 | func TestResolveAPIKeyCommandFailureDoesNotExposeOutput(t *testing.T) { | |
| 111 | 111 | resetAPIKeyCommandCache(t) | |
| 112 | - | t.Setenv("ZOT_API_KEY_COMMAND_HELPER", "1") | |
| 113 | - | t.Setenv("ZOT_API_KEY_COMMAND_VALUE", "must-not-appear") | |
| 112 | + | t.Setenv("ZUT_API_KEY_COMMAND_HELPER", "1") | |
| 113 | + | t.Setenv("ZUT_API_KEY_COMMAND_VALUE", "must-not-appear") | |
| 114 | 114 | ||
| 115 | 115 | _, err := ResolveAPIKeyCommand(context.Background(), apiKeyHelperCommand("fail")) | |
| 116 | 116 | if err == nil { | |
| @@ -123,7 +123,7 @@ func TestResolveAPIKeyCommandFailureDoesNotExposeOutput(t *testing.T) { | |||
| 123 | 123 | ||
| 124 | 124 | func TestResolveAPIKeyCommandTimeoutAndOutputLimit(t *testing.T) { | |
| 125 | 125 | resetAPIKeyCommandCache(t) | |
| 126 | - | t.Setenv("ZOT_API_KEY_COMMAND_HELPER", "1") | |
| 126 | + | t.Setenv("ZUT_API_KEY_COMMAND_HELPER", "1") | |
| 127 | 127 | ||
| 128 | 128 | t.Run("timeout", func(t *testing.T) { | |
| 129 | 129 | command := apiKeyHelperCommand("sleep") | |
M packages/provider/auth/assets/assets.go +4 -4
| @@ -1,13 +1,13 @@ | |||
| 1 | - | // Package assets holds static resources embedded in the zot binary. | |
| 2 | - | // Currently just the zot logo used by the tui welcome banner. | |
| 1 | + | // Package assets holds static resources embedded in the zut binary. | |
| 2 | + | // Currently just the zut logo used by the tui welcome banner. | |
| 3 | 3 | package assets | |
| 4 | 4 | ||
| 5 | 5 | import _ "embed" | |
| 6 | 6 | ||
| 7 | - | // LogoPNG is the pixel-art zot `z` logo as PNG bytes. | |
| 7 | + | // LogoPNG is the pixel-art zut `z` logo as PNG bytes. | |
| 8 | 8 | // Used by the interactive welcome banner; decoded once and rasterized | |
| 9 | 9 | // to Unicode half-blocks so it renders on any terminal without needing | |
| 10 | 10 | // inline image support. | |
| 11 | 11 | // | |
| 12 | - | //go:embed zot-logo.png | |
| 12 | + | //go:embed zut-logo.png | |
| 13 | 13 | var LogoPNG []byte | |
R packages/provider/auth/assets/zot-logo.png → packages/provider/auth/assets/zut-logo.png +0 -0 binary file: no text diff · renamed, 100% similar
binary file: no text diff · renamed, 100% similar
M packages/provider/auth/callback.go +11 -11
| @@ -10,7 +10,7 @@ import ( | |||
| 10 | 10 | "sync" | |
| 11 | 11 | "time" | |
| 12 | 12 | ||
| 13 | - | "github.com/patriceckhart/zot/packages/provider/auth/assets" | |
| 13 | + | "github.com/bnema/zut/packages/provider/auth/assets" | |
| 14 | 14 | ) | |
| 15 | 15 | ||
| 16 | 16 | // CallbackResult is what an OAuth callback server returns once the | |
| @@ -40,7 +40,7 @@ func NewCallbackServer(p OAuthProvider, expectedState string) (*CallbackServer, | |||
| 40 | 40 | addr := fmt.Sprintf("%s:%d", p.RedirectHost, p.RedirectPort) | |
| 41 | 41 | l, err := net.Listen("tcp", addr) | |
| 42 | 42 | if err != nil { | |
| 43 | - | return nil, fmt.Errorf("bind %s: %w (is another zot/claude-code/codex login already running?)", addr, err) | |
| 43 | + | return nil, fmt.Errorf("bind %s: %w (is another zut/claude-code/codex login already running?)", addr, err) | |
| 44 | 44 | } | |
| 45 | 45 | cs := &CallbackServer{ | |
| 46 | 46 | l: l, | |
| @@ -123,9 +123,9 @@ func (cs *CallbackServer) deliver(r CallbackResult) { | |||
| 123 | 123 | ||
| 124 | 124 | // ---- static HTML for the callback tab ---- | |
| 125 | 125 | ||
| 126 | - | // All zot-served pages share a single dark style matching the TUI: | |
| 126 | + | // All zut-served pages share a single dark style matching the TUI: | |
| 127 | 127 | // near-black background (#0a0a0a), white text, Geist Mono type, cyan | |
| 128 | - | // accent on the word "zot". Serves every step of both the api-key | |
| 128 | + | // accent on the word "zut". Serves every step of both the api-key | |
| 129 | 129 | // flow and the oauth callback flow. | |
| 130 | 130 | const monoStyle = `<style> | |
| 131 | 131 | @import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;600&display=swap'); | |
| @@ -155,7 +155,7 @@ const monoStyle = `<style> | |||
| 155 | 155 | letter-spacing: 0.01em; | |
| 156 | 156 | } | |
| 157 | 157 | h1 .mark { display: inline-block; width: 1.25rem; } | |
| 158 | - | .zot { color: #7ed3fc; } | |
| 158 | + | .zut { color: #7ed3fc; } | |
| 159 | 159 | .rule { border: 0; border-top: 1px solid #ffffff; margin: 1.5rem 0; } | |
| 160 | 160 | .muted { color: #9ca3af; } | |
| 161 | 161 | .mono { font-family: inherit; word-break: break-all; } | |
| @@ -179,9 +179,9 @@ const monoStyle = `<style> | |||
| 179 | 179 | button:hover { background: #0a0a0a; color: #ffffff; } | |
| 180 | 180 | </style>` | |
| 181 | 181 | ||
| 182 | - | // logoTag is the <img> element used at the top of every zot-served | |
| 182 | + | // logoTag is the <img> element used at the top of every zut-served | |
| 183 | 183 | // page. The image bytes are served from /logo.png by the same server. | |
| 184 | - | const logoTag = `<img class="logo" src="/logo.png" alt="zot" />` | |
| 184 | + | const logoTag = `<img class="logo" src="/logo.png" alt="zut" />` | |
| 185 | 185 | ||
| 186 | 186 | // serveLogo writes the embedded PNG with appropriate caching headers. | |
| 187 | 187 | // Shared between the oauth callback server and the api-key login server. | |
| @@ -193,21 +193,21 @@ func serveLogo(w http.ResponseWriter, r *http.Request) { | |||
| 193 | 193 | ||
| 194 | 194 | func oauthSuccessHTML(provider string) string { | |
| 195 | 195 | p := strings.ToLower(provider) | |
| 196 | - | return `<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zot - logged in</title>` + monoStyle + `</head><body> | |
| 196 | + | return `<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zut - logged in</title>` + monoStyle + `</head><body> | |
| 197 | 197 | ` + logoTag + ` | |
| 198 | 198 | <h1><span class="mark">✓</span> logged in to ` + p + `</h1> | |
| 199 | 199 | <hr class="rule"> | |
| 200 | - | <p class="msg"><span class="zot">zot</span> received the callback. you can close this tab.</p> | |
| 200 | + | <p class="msg"><span class="zut">zut</span> received the callback. you can close this tab.</p> | |
| 201 | 201 | </body></html>` | |
| 202 | 202 | } | |
| 203 | 203 | ||
| 204 | 204 | func oauthErrorHTML(msg string) string { | |
| 205 | - | return `<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zot - error</title>` + monoStyle + `</head><body> | |
| 205 | + | return `<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zut - error</title>` + monoStyle + `</head><body> | |
| 206 | 206 | ` + logoTag + ` | |
| 207 | 207 | <h1><span class="mark">✗</span> login failed</h1> | |
| 208 | 208 | <hr class="rule"> | |
| 209 | 209 | <p class="msg mono">` + htmlEscape(msg) + `</p> | |
| 210 | - | <p class="muted">go back to <span class="zot">zot</span> and try again.</p> | |
| 210 | + | <p class="muted">go back to <span class="zut">zut</span> and try again.</p> | |
| 211 | 211 | </body></html>` | |
| 212 | 212 | } | |
| 213 | 213 | ||
M packages/provider/auth/kimi.go +2 -2
| @@ -30,7 +30,7 @@ func RequestKimiDeviceAuthorization(ctx context.Context) (KimiDeviceAuthorizatio | |||
| 30 | 30 | } | |
| 31 | 31 | req.Header.Set("content-type", "application/x-www-form-urlencoded") | |
| 32 | 32 | req.Header.Set("accept", "application/json") | |
| 33 | - | req.Header.Set("user-agent", "zot") | |
| 33 | + | req.Header.Set("user-agent", "zut") | |
| 34 | 34 | ||
| 35 | 35 | resp, err := (&http.Client{Timeout: 30 * time.Second}).Do(req) | |
| 36 | 36 | if err != nil { | |
| @@ -77,7 +77,7 @@ func pollKimiDeviceTokenOnce(ctx context.Context, deviceCode string) (*OAuthToke | |||
| 77 | 77 | } | |
| 78 | 78 | req.Header.Set("content-type", "application/x-www-form-urlencoded") | |
| 79 | 79 | req.Header.Set("accept", "application/json") | |
| 80 | - | req.Header.Set("user-agent", "zot") | |
| 80 | + | req.Header.Set("user-agent", "zut") | |
| 81 | 81 | resp, err := (&http.Client{Timeout: 30 * time.Second}).Do(req) | |
| 82 | 82 | if err != nil { | |
| 83 | 83 | return nil, 0, fmt.Errorf("kimi token poll: %w", err) | |
M packages/provider/auth/manager.go +2 -2
| @@ -453,10 +453,10 @@ func parseManualCodeInput(s string) (code, state string) { | |||
| 453 | 453 | // (containers, SSH without display forwarding, etc.) instead of | |
| 454 | 454 | // trying to bind a callback port the user can never reach. | |
| 455 | 455 | func HasBrowser() bool { | |
| 456 | - | if os.Getenv("ZOT_NO_BROWSER") != "" { | |
| 456 | + | if os.Getenv("ZUT_NO_BROWSER") != "" { | |
| 457 | 457 | return false | |
| 458 | 458 | } | |
| 459 | - | if os.Getenv("ZOT_FORCE_BROWSER") != "" { | |
| 459 | + | if os.Getenv("ZUT_FORCE_BROWSER") != "" { | |
| 460 | 460 | return true | |
| 461 | 461 | } | |
| 462 | 462 | if _, err := os.Stat("/.dockerenv"); err == nil { | |
M packages/provider/auth/oauth.go +5 -3
| @@ -20,7 +20,7 @@ import ( | |||
| 20 | 20 | // WARNING: the client ids used here belong to the official first-party | |
| 21 | 21 | // CLIs (Claude Code for Anthropic, Codex CLI for OpenAI). Using them from | |
| 22 | 22 | // third-party software is against the respective terms of service and | |
| 23 | - | // may be revoked at any time. zot exposes this only behind | |
| 23 | + | // may be revoked at any time. zut exposes this only behind | |
| 24 | 24 | // --experimental-oauth. | |
| 25 | 25 | type OAuthProvider struct { | |
| 26 | 26 | Name string | |
| @@ -78,7 +78,7 @@ var ( | |||
| 78 | 78 | ||
| 79 | 79 | // Anthropic manual / headless variant: redirects to Anthropic's | |
| 80 | 80 | // copy-code page instead of a local loopback port, used when there | |
| 81 | - | // is no browser on the machine running zot (e.g. inside a Docker | |
| 81 | + | // is no browser on the machine running zut (e.g. inside a Docker | |
| 82 | 82 | // container or over plain SSH). | |
| 83 | 83 | AnthropicManualOAuth = OAuthProvider{ | |
| 84 | 84 | Name: "anthropic", | |
| @@ -108,7 +108,9 @@ var ( | |||
| 108 | 108 | ExtraAuthArgs: map[string]string{ | |
| 109 | 109 | "id_token_add_organizations": "true", | |
| 110 | 110 | "codex_cli_simplified_flow": "true", | |
| 111 | - | "originator": "zot", | |
| 111 | + | // OpenAI's registered Codex OAuth flow requires this legacy | |
| 112 | + | // originator value; it is not a zut product identifier. | |
| 113 | + | "originator": "zot", | |
| 112 | 114 | }, | |
| 113 | 115 | } | |
| 114 | 116 | ||
M packages/provider/auth/server.go +11 -11
| @@ -270,11 +270,11 @@ func Redirect(w http.ResponseWriter, r *http.Request, u *url.URL) { | |||
| 270 | 270 | // the browser tab looks like the tui: black on white, monospace, thin | |
| 271 | 271 | // rules, no rounded boxes, no color. | |
| 272 | 272 | ||
| 273 | - | var tpl = template.Must(template.New("index").Parse(`<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zot login</title>` + monoStyle + `</head><body> | |
| 273 | + | var tpl = template.Must(template.New("index").Parse(`<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zut login</title>` + monoStyle + `</head><body> | |
| 274 | 274 | ` + logoTag + ` | |
| 275 | - | <h1><span class="zot">zot</span> login</h1> | |
| 275 | + | <h1><span class="zut">zut</span> login</h1> | |
| 276 | 276 | <hr class="rule"> | |
| 277 | - | <p>paste an api key for anthropic, openai, kimi, deepseek, or google. <span class="zot">zot</span> probes the provider once, then saves the key to <span class="mono">~/Library/Application Support/zot/auth.json</span>.</p> | |
| 277 | + | <p>paste an api key for anthropic, openai, kimi, deepseek, or google. <span class="zut">zut</span> probes the provider once, then saves the key to <span class="mono">~/Library/Application Support/zut/auth.json</span>.</p> | |
| 278 | 278 | <p> | |
| 279 | 279 | <a href="/apikey?provider=anthropic">anthropic api key →</a><br> | |
| 280 | 280 | <a href="/apikey?provider=openai">openai api key →</a><br> | |
| @@ -283,18 +283,18 @@ var tpl = template.Must(template.New("index").Parse(`<!doctype html><html lang=" | |||
| 283 | 283 | <a href="/apikey?provider=google">google gemini api key →</a> | |
| 284 | 284 | </p> | |
| 285 | 285 | <hr class="rule"> | |
| 286 | - | <p class="muted">for a subscription login (claude pro/max - chatgpt plus/pro - kimi code - github copilot), close this tab and run /login inside <span class="zot">zot</span>.</p> | |
| 286 | + | <p class="muted">for a subscription login (claude pro/max - chatgpt plus/pro - kimi code - github copilot), close this tab and run /login inside <span class="zut">zut</span>.</p> | |
| 287 | 287 | </body></html>`)) | |
| 288 | 288 | ||
| 289 | 289 | func init() { | |
| 290 | - | template.Must(tpl.New("apikey").Parse(`<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zot login</title>` + monoStyle + `<style> | |
| 290 | + | template.Must(tpl.New("apikey").Parse(`<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zut login</title>` + monoStyle + `<style> | |
| 291 | 291 | form { display: flex; flex-direction: column; gap: 0.75rem; } | |
| 292 | 292 | label { font-size: 0.875rem; } | |
| 293 | 293 | </style></head><body> | |
| 294 | 294 | ` + logoTag + ` | |
| 295 | - | <h1><span class="zot">zot</span> login - {{.Provider}} api key</h1> | |
| 295 | + | <h1><span class="zut">zut</span> login - {{.Provider}} api key</h1> | |
| 296 | 296 | <hr class="rule"> | |
| 297 | - | <p>paste your {{.Provider}} api key. <span class="zot">zot</span> will probe the provider with it once, then save it if the key is accepted.</p> | |
| 297 | + | <p>paste your {{.Provider}} api key. <span class="zut">zut</span> will probe the provider with it once, then save it if the key is accepted.</p> | |
| 298 | 298 | <form method="POST" action="/apikey"> | |
| 299 | 299 | <input type="hidden" name="provider" value="{{.Provider}}" /> | |
| 300 | 300 | <label for="api_key">api key</label> | |
| @@ -303,19 +303,19 @@ func init() { | |||
| 303 | 303 | </form> | |
| 304 | 304 | </body></html>`)) | |
| 305 | 305 | ||
| 306 | - | template.Must(tpl.New("success").Parse(`<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zot - logged in</title>` + monoStyle + `</head><body> | |
| 306 | + | template.Must(tpl.New("success").Parse(`<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zut - logged in</title>` + monoStyle + `</head><body> | |
| 307 | 307 | ` + logoTag + ` | |
| 308 | 308 | <h1><span class="mark">✓</span> logged in to {{.Provider}}</h1> | |
| 309 | 309 | <hr class="rule"> | |
| 310 | 310 | <p class="msg">method: {{.Method}}</p> | |
| 311 | - | <p class="muted"><span class="zot">zot</span> received the callback. you can close this tab and return to the terminal.</p> | |
| 311 | + | <p class="muted"><span class="zut">zut</span> received the callback. you can close this tab and return to the terminal.</p> | |
| 312 | 312 | </body></html>`)) | |
| 313 | 313 | ||
| 314 | - | template.Must(tpl.New("error").Parse(`<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zot - error</title>` + monoStyle + `</head><body> | |
| 314 | + | template.Must(tpl.New("error").Parse(`<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>zut - error</title>` + monoStyle + `</head><body> | |
| 315 | 315 | ` + logoTag + ` | |
| 316 | 316 | <h1><span class="mark">✗</span> login failed</h1> | |
| 317 | 317 | <hr class="rule"> | |
| 318 | 318 | <p class="msg mono">{{.Message}}</p> | |
| 319 | - | <p class="muted">go back to <span class="zot">zot</span> and try again.</p> | |
| 319 | + | <p class="muted">go back to <span class="zut">zut</span> and try again.</p> | |
| 320 | 320 | </body></html>`)) | |
| 321 | 321 | } | |
M packages/provider/auth/store.go +2 -2
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | // Package auth handles credential storage and the login methods | |
| 2 | - | // supported by zot: API keys and provider-specific subscription OAuth. | |
| 2 | + | // supported by zut: API keys and provider-specific subscription OAuth. | |
| 3 | 3 | // | |
| 4 | - | // All credentials live in $ZOT_HOME/auth.json (mode 0600). | |
| 4 | + | // All credentials live in $ZUT_HOME/auth.json (mode 0600). | |
| 5 | 5 | package auth | |
| 6 | 6 | ||
| 7 | 7 | import ( | |
M packages/provider/catalog_builtin.go +1 -1
| @@ -6,7 +6,7 @@ package provider | |||
| 6 | 6 | // | |
| 7 | 7 | // This file is data; the values are tracked verbatim from each | |
| 8 | 8 | // vendor's own public pricing / model-list pages. They drift; users | |
| 9 | - | // override via $ZOT_HOME/models.json (user entries win). | |
| 9 | + | // override via $ZUT_HOME/models.json (user entries win). | |
| 10 | 10 | // | |
| 11 | 11 | // Curated entries in models.go (anthropic / openai / openai-codex / | |
| 12 | 12 | // the small seed list for kimi / deepseek / google) are not duplicated | |
M packages/provider/extra_models.go +1 -1
| @@ -3,7 +3,7 @@ package provider | |||
| 3 | 3 | // Hand-curated catalog supplements that the bulk builtin catalog | |
| 4 | 4 | // (catalog_builtin.go) doesn't cover. | |
| 5 | 5 | // | |
| 6 | - | // - openai-responses: zot-specific provider id for the public OpenAI | |
| 6 | + | // - openai-responses: zut-specific provider id for the public OpenAI | |
| 7 | 7 | // Responses API (api.openai.com/v1/responses). Separate from | |
| 8 | 8 | // `openai` (Chat Completions) and `openai-codex` (ChatGPT subscription). | |
| 9 | 9 | // - kimi/kimi-k2-thinking: matches the Anthropic-Messages endpoint | |
M packages/provider/extra_providers.go +1 -1
| @@ -326,7 +326,7 @@ func NewCloudflareAIGateway(apiKey, baseURL string) Client { | |||
| 326 | 326 | ||
| 327 | 327 | // NewGithubCopilot returns a GitHub Copilot client. The provided | |
| 328 | 328 | // credential must be a GitHub Personal Access Token (PAT) with Copilot | |
| 329 | - | // access enabled; zot trades it for a short-lived Copilot token on | |
| 329 | + | // access enabled; zut trades it for a short-lived Copilot token on | |
| 330 | 330 | // every inference request (cached in memory until ~5min before expiry). | |
| 331 | 331 | // | |
| 332 | 332 | // Wire format: OpenAI Chat Completions. Copilot-specific headers | |
M packages/provider/gemini.go +4 -4
| @@ -25,7 +25,7 @@ import ( | |||
| 25 | 25 | // consumer Gemini Advanced / Google One AI subscriptions; programmatic | |
| 26 | 26 | // access requires either an AI Studio API key (this client) or Vertex | |
| 27 | 27 | // AI / GCP service-account credentials (separate provider, not yet | |
| 28 | - | // implemented in zot). | |
| 28 | + | // implemented in zut). | |
| 29 | 29 | ||
| 30 | 30 | const geminiDefaultBaseURL = "https://generativelanguage.googleapis.com" | |
| 31 | 31 | ||
| @@ -54,7 +54,7 @@ func (c *geminiClient) Name() string { return "google" } | |||
| 54 | 54 | // ---- wire types ---- | |
| 55 | 55 | // | |
| 56 | 56 | // Subset of Gemini's Content / Part / GenerateContentRequest schema. | |
| 57 | - | // Only the fields zot actually emits or consumes are declared here. | |
| 57 | + | // Only the fields zut actually emits or consumes are declared here. | |
| 58 | 58 | ||
| 59 | 59 | type gemInlineData struct { | |
| 60 | 60 | MimeType string `json:"mimeType"` | |
| @@ -352,7 +352,7 @@ func saveGeminiImageToWorkingDir(mimeType string, data []byte) (string, error) { | |||
| 352 | 352 | case "image/gif": | |
| 353 | 353 | ext = ".gif" | |
| 354 | 354 | } | |
| 355 | - | name := "zot-gemini-image-" + uuid.NewString() + ext | |
| 355 | + | name := "zut-gemini-image-" + uuid.NewString() + ext | |
| 356 | 356 | path := filepath.Join(".", name) | |
| 357 | 357 | if err := os.WriteFile(path, data, 0o644); err != nil { | |
| 358 | 358 | return "", err | |
| @@ -406,7 +406,7 @@ func convertGemToolResultParts(blocks []Content) []gemPart { | |||
| 406 | 406 | return parts | |
| 407 | 407 | } | |
| 408 | 408 | ||
| 409 | - | // geminiThinkingConfig maps zot's reasoning level ("low"/"medium"/"high") | |
| 409 | + | // geminiThinkingConfig maps zut's reasoning level ("low"/"medium"/"high") | |
| 410 | 410 | // to Gemini's thinkingConfig. The right knob depends on the model | |
| 411 | 411 | // generation: 2.5 family uses thinkingBudget (tokens), 3.x uses | |
| 412 | 412 | // thinkingLevel (enum). Returns nil when the level is unrecognised. | |
M packages/provider/gemini_test.go +4 -4
| @@ -111,7 +111,7 @@ func TestGeminiStreamInlineImage(t *testing.T) { | |||
| 111 | 111 | t.Fatalf("image=%q %q", ib.MimeType, string(ib.Data)) | |
| 112 | 112 | } | |
| 113 | 113 | saved, ok := done.Message.Content[2].(TextBlock) | |
| 114 | - | if !ok || !strings.Contains(saved.Text, "zot-gemini-image-") || !strings.Contains(saved.Text, ".png") { | |
| 114 | + | if !ok || !strings.Contains(saved.Text, "zut-gemini-image-") || !strings.Contains(saved.Text, ".png") { | |
| 115 | 115 | t.Fatalf("saved path block=%T %+v", done.Message.Content[2], done.Message.Content[2]) | |
| 116 | 116 | } | |
| 117 | 117 | path := strings.TrimPrefix(saved.Text, "Saved image: `") | |
| @@ -184,7 +184,7 @@ func TestGeminiBuildRequestSystemAndTools(t *testing.T) { | |||
| 184 | 184 | c := NewGemini("k", "https://example.invalid").(*geminiClient) | |
| 185 | 185 | wire, _, err := c.buildRequest(Request{ | |
| 186 | 186 | Model: "gemini-2.5-pro", | |
| 187 | - | System: "you are zot", | |
| 187 | + | System: "you are zut", | |
| 188 | 188 | Tools: []Tool{ | |
| 189 | 189 | {Name: "read", Description: "read a file", Schema: json.RawMessage(`{"type":"object","properties":{"path":{"type":"string"}}}`)}, | |
| 190 | 190 | }, | |
| @@ -193,7 +193,7 @@ func TestGeminiBuildRequestSystemAndTools(t *testing.T) { | |||
| 193 | 193 | if err != nil { | |
| 194 | 194 | t.Fatal(err) | |
| 195 | 195 | } | |
| 196 | - | if wire.SystemInstruction == nil || wire.SystemInstruction.Parts[0].Text != "you are zot" { | |
| 196 | + | if wire.SystemInstruction == nil || wire.SystemInstruction.Parts[0].Text != "you are zut" { | |
| 197 | 197 | t.Fatalf("system: %+v", wire.SystemInstruction) | |
| 198 | 198 | } | |
| 199 | 199 | if len(wire.Tools) != 1 || len(wire.Tools[0].FunctionDeclarations) != 1 || wire.Tools[0].FunctionDeclarations[0].Name != "read" { | |
| @@ -537,7 +537,7 @@ func TestGeminiStreamReasoning(t *testing.T) { | |||
| 537 | 537 | } | |
| 538 | 538 | } | |
| 539 | 539 | ||
| 540 | - | // TestGeminiBuildRequestWithReasoningReplay verifies that when zot sends a | |
| 540 | + | // TestGeminiBuildRequestWithReasoningReplay verifies that when zut sends a | |
| 541 | 541 | // historical assistant message containing a ReasoningBlock, buildRequest | |
| 542 | 542 | // serializes it into the verbatim Gemini "thought: true" wire representation | |
| 543 | 543 | // with its corresponding thoughtSignature. | |
M packages/provider/github_copilot.go +2 -2
| @@ -5,7 +5,7 @@ package provider | |||
| 5 | 5 | // Auth chain: | |
| 6 | 6 | // | |
| 7 | 7 | // 1. User stores a GitHub Personal Access Token (PAT) in | |
| 8 | - | // COPILOT_GITHUB_TOKEN (or via zot's auth.json, when wired). The PAT | |
| 8 | + | // COPILOT_GITHUB_TOKEN (or via zut's auth.json, when wired). The PAT | |
| 9 | 9 | // is the long-lived credential; it never hits the inference endpoint. | |
| 10 | 10 | // 2. We trade the PAT for a short-lived Copilot token by hitting | |
| 11 | 11 | // `GET https://api.github.com/copilot_internal/v2/token` with the PAT | |
| @@ -19,7 +19,7 @@ package provider | |||
| 19 | 19 | // - X-Initiator: user|agent | |
| 20 | 20 | // - Openai-Intent: conversation-edits | |
| 21 | 21 | // - Copilot-Vision-Request: true (when images present; not wired | |
| 22 | - | // here because the zot openai client currently sends images inline) | |
| 22 | + | // here because the zut openai client currently sends images inline) | |
| 23 | 23 | // | |
| 24 | 24 | // Token caching: short-lived tokens last ~30min. We cache one per PAT in | |
| 25 | 25 | // memory for the process lifetime and refresh on demand. No disk cache. | |
M packages/provider/google_vertex.go +1 -1
| @@ -52,7 +52,7 @@ import ( | |||
| 52 | 52 | "sync" | |
| 53 | 53 | "time" | |
| 54 | 54 | ||
| 55 | - | providerauth "github.com/patriceckhart/zot/packages/provider/auth" | |
| 55 | + | providerauth "github.com/bnema/zut/packages/provider/auth" | |
| 56 | 56 | ) | |
| 57 | 57 | ||
| 58 | 58 | type vertexConfig struct { | |
M packages/provider/llamacpp.go +1 -1
| @@ -419,7 +419,7 @@ func (c *LlamaCPPClient) DownloadAndWait(ctx context.Context, model string, upda | |||
| 419 | 419 | } | |
| 420 | 420 | } | |
| 421 | 421 | ||
| 422 | - | // LlamaCPPModels converts loaded router entries into zot model metadata. | |
| 422 | + | // LlamaCPPModels converts loaded router entries into zut model metadata. | |
| 423 | 423 | func LlamaCPPModels(models []LlamaCPPModel, serverURL string) []Model { | |
| 424 | 424 | baseURL, err := LlamaCPPInferenceURL(serverURL) | |
| 425 | 425 | if err != nil { | |
M packages/provider/models.go +1 -1
| @@ -60,7 +60,7 @@ type Model struct { | |||
| 60 | 60 | } | |
| 61 | 61 | ||
| 62 | 62 | // Catalog is the hardcoded, read-only list of supported models. | |
| 63 | - | // Prices are USD per 1M tokens. The list is curated to what zot's | |
| 63 | + | // Prices are USD per 1M tokens. The list is curated to what zut's | |
| 64 | 64 | // clients (Anthropic Messages + OpenAI Chat Completions) can actually | |
| 65 | 65 | // talk to; models that are only reachable through the OpenAI Responses | |
| 66 | 66 | // API (o1-pro, o3-pro, gpt-5-pro) are omitted. | |
M packages/provider/openai.go +2 -2
| @@ -261,7 +261,7 @@ func (c *openaiClient) buildRequest(req Request) (*oaiRequest, error) { | |||
| 261 | 261 | // Some gateways expose adaptive-thinking Anthropic models through | |
| 262 | 262 | // the OpenAI-compatible chat-completions wire. They accept the | |
| 263 | 263 | // same reasoning_effort knob, including the top "xhigh" tier; | |
| 264 | - | // don't clamp zot's "maximum" to "high" for those models. | |
| 264 | + | // don't clamp zut's "maximum" to "high" for those models. | |
| 265 | 265 | effort = OpenAICompatAnthropicEffort(reasoning) | |
| 266 | 266 | } | |
| 267 | 267 | if effort != "" { | |
| @@ -341,7 +341,7 @@ func (c *openaiClient) buildRequest(req Request) (*oaiRequest, error) { | |||
| 341 | 341 | } | |
| 342 | 342 | // Kimi rejects assistant messages with neither visible text nor | |
| 343 | 343 | // tool calls ("assistant must not be empty"). This can happen when | |
| 344 | - | // a previous stream produced only reasoning_content, which zot keeps | |
| 344 | + | // a previous stream produced only reasoning_content, which zut keeps | |
| 345 | 345 | // internally for provider replay but cannot send back as standalone | |
| 346 | 346 | // assistant content on OpenAI-compatible chat-completions APIs. | |
| 347 | 347 | if am.Content == nil && len(am.ToolCalls) == 0 { | |
M packages/provider/openai_codex.go +5 -3
| @@ -21,7 +21,7 @@ import ( | |||
| 21 | 21 | // Wire protocol notes: | |
| 22 | 22 | // - Endpoint: POST https://chatgpt.com/backend-api/codex/responses | |
| 23 | 23 | // - Headers: Authorization: Bearer <access_token>, chatgpt-account-id: <id>, | |
| 24 | - | // OpenAI-Beta: responses=experimental, originator: zot | |
| 24 | + | // OpenAI-Beta: responses=experimental, originator: zot (required legacy value) | |
| 25 | 25 | // - Body: OpenAI Responses API shape (not chat/completions). | |
| 26 | 26 | // input: [{role, content: [{type: "input_text" | "input_image" | ... }]}] | |
| 27 | 27 | // instructions: <system prompt> | |
| @@ -75,7 +75,7 @@ func NewOpenAICodex(token, accountID, baseURL string) Client { | |||
| 75 | 75 | ||
| 76 | 76 | func (c *codexClient) Name() string { return "openai-codex" } | |
| 77 | 77 | ||
| 78 | - | // ---- Responses API wire types (subset needed for zot's surface) ---- | |
| 78 | + | // ---- Responses API wire types (subset needed for zut's surface) ---- | |
| 79 | 79 | ||
| 80 | 80 | type codexInputText struct { | |
| 81 | 81 | Type string `json:"type"` // "input_text" | |
| @@ -360,7 +360,7 @@ func usesCodexCLIRouting(model string) bool { | |||
| 360 | 360 | func newCodexSessionID() string { | |
| 361 | 361 | var b [16]byte | |
| 362 | 362 | if _, err := rand.Read(b[:]); err != nil { | |
| 363 | - | return fmt.Sprintf("zot-%d", time.Now().UnixNano()) | |
| 363 | + | return fmt.Sprintf("zut-%d", time.Now().UnixNano()) | |
| 364 | 364 | } | |
| 365 | 365 | return strings.Join([]string{ | |
| 366 | 366 | hex.EncodeToString(b[0:4]), | |
| @@ -410,6 +410,8 @@ func (c *codexClient) Stream(ctx context.Context, req Request) (<-chan Event, er | |||
| 410 | 410 | httpReq.Header.Set("session-id", codexCLISessionID) | |
| 411 | 411 | httpReq.Header.Set("user-agent", "codex_cli_rs/0.0.0") | |
| 412 | 412 | } else { | |
| 413 | + | // ChatGPT's backend recognizes these established values. They are | |
| 414 | + | // external provider metadata, not zut product identifiers. | |
| 413 | 415 | httpReq.Header.Set("originator", "zot") | |
| 414 | 416 | httpReq.Header.Set("user-agent", fmt.Sprintf("zot (%s %s)", runtime.GOOS, runtime.GOARCH)) | |
| 415 | 417 | } | |
M packages/provider/openai_codex_test.go +7 -0
| @@ -6,6 +6,7 @@ import ( | |||
| 6 | 6 | "encoding/json" | |
| 7 | 7 | "io" | |
| 8 | 8 | "net/http" | |
| 9 | + | "runtime" | |
| 9 | 10 | "strings" | |
| 10 | 11 | "testing" | |
| 11 | 12 | ) | |
| @@ -175,6 +176,12 @@ func TestOpenAIGPT56DoesNotUseCodexCLIRouting(t *testing.T) { | |||
| 175 | 176 | if gotBody.PromptCacheKey != "" { | |
| 176 | 177 | t.Fatalf("prompt_cache_key = %q", gotBody.PromptCacheKey) | |
| 177 | 178 | } | |
| 179 | + | if gotReq.Header.Get("originator") != "zot" { | |
| 180 | + | t.Fatalf("originator = %q", gotReq.Header.Get("originator")) | |
| 181 | + | } | |
| 182 | + | if gotReq.Header.Get("user-agent") != "zot ("+runtime.GOOS+" "+runtime.GOARCH+")" { | |
| 183 | + | t.Fatalf("user-agent = %q", gotReq.Header.Get("user-agent")) | |
| 184 | + | } | |
| 178 | 185 | } | |
| 179 | 186 | ||
| 180 | 187 | func TestCodexNestedStreamError(t *testing.T) { | |
M packages/provider/provider.go +2 -2
| @@ -1,4 +1,4 @@ | |||
| 1 | - | // Package provider defines the LLM client abstraction used by zot. | |
| 1 | + | // Package provider defines the LLM client abstraction used by zut. | |
| 2 | 2 | // | |
| 3 | 3 | // It supports exactly two providers: Anthropic (Messages API) and | |
| 4 | 4 | // OpenAI (Chat Completions API). Everything above this package operates | |
| @@ -69,7 +69,7 @@ func (ToolResultBlock) isContent() {} | |||
| 69 | 69 | // providers that require it on follow-up requests (OpenAI Codex with | |
| 70 | 70 | // thinking enabled) can replay the same payload they emitted earlier. | |
| 71 | 71 | // Summary is the human-readable reasoning summary (may be empty); the | |
| 72 | - | // encrypted blob is opaque to zot. ID is the provider-issued reasoning | |
| 72 | + | // encrypted blob is opaque to zut. ID is the provider-issued reasoning | |
| 73 | 73 | // item id. | |
| 74 | 74 | type ReasoningBlock struct { | |
| 75 | 75 | ID string `json:"reasoning_id,omitempty"` | |
M packages/provider/provider_test.go +1 -1 collapsed to keep the page light
M packages/provider/reasoning.go +6 -6 collapsed to keep the page light
M packages/provider/usermodels.go +1 -1
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // UserModelsFile is the JSON format for user-defined models. | |
| 12 | - | // Place a models.json in $ZOT_HOME to add models that aren't in the | |
| 12 | + | // Place a models.json in $ZUT_HOME to add models that aren't in the | |
| 13 | 13 | // baked-in catalog or to override catalog entries. Custom providers | |
| 14 | 14 | // (not in the built-in set) may specify a baseUrl and api format at | |
| 15 | 15 | // the provider level: | |