1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
---
name: dotslash
description: Use when adding, updating, or troubleshooting DotSlash binaries in bin/ — covers file creation, Rakefile integration, and the dotslash CLI helpers
---
# DotSlash Binaries
This repo uses [DotSlash](https://dotslash-cli.com) to manage binaries in `bin/`. DotSlash files are lightweight JSON wrappers that lazily fetch, verify, cache, and run executables. Each file covers `macos-aarch64`, `linux-aarch64`, and `linux-x86_64`, minus any platform upstream doesn't publish. Run `dotslash --help` for the full CLI; `create-url-entry` is the only subcommand these workflows use.
## Adding a new DotSlash binary
The `update_dotslash_release` helper in `Rakefile` generates the file for you — you rarely hand-write the JSON.
### 1. Add a Rake task
In the `dotslash` namespace, add a task that calls the helper, then list `<name>` in the `all` task. The helper resolves the latest tag with `gh release view`, runs `create-url-entry`, and writes `bin/<name>`:
```ruby
desc "Update jj"
task(:jj) do
update_dotslash_release(name: "jj", repo: "jj-vcs/jj") do |tag|
{
"macos-aarch64" => "jj-#{tag}-aarch64-apple-darwin.tar.gz",
"linux-aarch64" => "jj-#{tag}-aarch64-unknown-linux-musl.tar.gz",
"linux-x86_64" => "jj-#{tag}-x86_64-unknown-linux-musl.tar.gz",
}
end
end
```
The block receives the resolved tag and returns a hash of DotSlash platform to asset filename. Ignore the tag when the names are fixed, or interpolate it (as above). Leave out a platform upstream doesn't publish. On Linux, prefer a `musl` asset over `gnu` when both exist, since it's statically linked and runs regardless of the host's glibc.
Pass `path:` when the executable sits inside an archive subdirectory; it defaults to `name`, which is correct when the archive holds a single file at its root. When the subdirectory varies by platform or version (e.g. `cq-0.11.0-aarch64-apple-darwin/cq`), pass `path:` a proc — it receives each asset filename, so the generated `path` stays correct across platforms and release bumps:
```ruby
path: ->(asset) { "#{asset.delete_suffix(".tar.gz")}/cq" }
```
### 2. Generate and verify
```bash
rake dotslash:<name>
chmod +x bin/<name>
bin/<name> --help # or whatever smoke test is appropriate
```
### Hand-writing the file (fallback)
Prefer extending `update_dotslash_release` over hand-writing JSON. Reserve hand-writing for cases the helper can't reach — a non-GitHub host, an unusual URL scheme, or a repo with no Rakefile to hold the helper (`Gusto/claude-code` ships DotSlash launchers inside plugins and has none):
1. Find the asset with `gh release view --repo owner/repo --json tagName,assets` and pick the asset for each platform (e.g. `darwin-arm64`, `linux-arm64`, `linux-amd64`).
2. Run `dotslash -- create-url-entry URL`. It computes size and hash and infers `format` from the URL suffix; you fill in `path`.
On a private repo it can't do this — it fetches the URL with no auth and gets a 404. Download the asset instead (`gh release download <tag> --repo owner/repo --pattern '<asset>'`), then take `size` from `ls -l` and `digest` from `shasum -a 256`, and set `"hash": "sha256"` to match.
3. Determine `path`: for a bare binary, the filename to save as (e.g. `"jq"`, omit `format`); for an archive, the executable's relative path inside it (list contents with `curl -sL URL | tar tzf -`, or `tar tzf <file>` on the copy you downloaded above).
4. Write `bin/<name>` with the structure below, then `chmod +x`:
```
#!/usr/bin/env dotslash
{
"name": "<name>",
"platforms": {
"macos-aarch64": {
"size": <size>,
"hash": "blake3",
"digest": "<digest>",
"format": "<format>", // omit for bare binaries
"path": "<path>",
"providers": [
{
"url": "<url>"
},
{
"type": "github-release",
"repo": "https://github.com/owner/repo",
"tag": "<tag>",
"name": "<asset-filename>"
}
]
},
"linux-aarch64": { ... },
"linux-x86_64": { ... }
}
}
```
Include both an HTTP provider (direct URL, tried first) and a GitHub Release provider (uses `gh` CLI, works with private repos). On a private repo, omit the HTTP provider — `releases/download/...` needs auth the bare `url` provider never sends, so it is a dead entry that masks the real error when the fallback breaks.
## Updating existing binaries
Run:
```bash
rake dotslash:all # every binary
rake dotslash:<name> # a single binary
```
## Key details
- Use `blake3` (preferred) or `sha256` for the hash. The `create-url-entry` helper defaults to blake3.
- The `format` field must be a recognized value: `tar.gz`, `tar.zst`, `tar.xz`, `tar.bz2`, `tar`, `zip`, `gz`, `zst`, `xz`, `bz2`. `tgz` is not valid; use `tar.gz`.
- The `path` must be a normalized relative UNIX path: no leading `./`, no `..`, no trailing `/`, no backslashes.
- Release tags containing `+` (e.g. `v2026-07-01+564c15b`) work unescaped in the provider URL — GitHub's download endpoint accepts them raw.