dotslash: add proc-form paths for version-named archives
cq packages its executable inside a version-named archive directory
(cq-<version>-aarch64-apple-darwin/cq), so the path in bin/cq changes
every release. Extended update_dotslash_release to accept a proc that
receives the resolved tag, matching the asset block, so bin/cq
regenerates a correct path after each release bump.
Documented the proc form in the skill so future additions reach for
it instead of hand-editing bin/ files every bump, and trimmed stale,
redundant content (an out-of-date binary list, a CLI table duplicating
--help) while touching the doc.
Assisted-by: Claude Sonnet 5 via Claude Code
diff --git a/Rakefile b/Rakefile
index 016a14c8..9bf285e9 100644
--- a/Rakefile
+++ b/Rakefile
@@ -52,6 +52,7 @@ namespace :dotslash do
tag = JSON.parse(`gh release view --repo #{repo} --json tagName`)["tagName"]
end
asset_name = asset.call(tag)
+ resolved_path = path.respond_to?(:call) ? path.call(tag) : path
url = "https://github.com/#{repo}/releases/download/#{tag}/#{asset_name}"
entry = JSON.parse(`dotslash -- create-url-entry #{url}`)
@@ -60,7 +61,7 @@ namespace :dotslash do
size: entry["size"],
hash: "blake3",
digest: entry["digest"],
- path: path,
+ path: resolved_path,
providers: [
{ url: url },
{ type: "github-release", repo: "https://github.com/#{repo}", tag: tag, name: asset_name },
@@ -122,10 +123,16 @@ namespace :dotslash do
update_dotslash_release(name: "ramekin", repo: "kejadlen/ramekin") { "ramekin-aarch64-apple-darwin.tar.gz" }
end
+ desc "Update cq"
+ task(:cq) do
+ update_dotslash_release(name: "cq", repo: "technicalpickles/cq", path: ->(tag) { "cq-#{tag.delete_prefix("v")}-aarch64-apple-darwin/cq" }) { |tag| "cq-#{tag.delete_prefix("v")}-aarch64-apple-darwin.tar.gz" }
+ end
+
desc "Update all dotslash files"
task all: %i[
age
age-keygen
+ cq
fzf
jj
jq
diff --git a/ai/skills/dotslash/SKILL.md b/ai/skills/dotslash/SKILL.md
index 6d509f4f..25c69eba 100644
--- a/ai/skills/dotslash/SKILL.md
+++ b/ai/skills/dotslash/SKILL.md
@@ -5,12 +5,7 @@ description: Use when adding, updating, or troubleshooting DotSlash binaries in
# 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. Only `macos-aarch64` platform entries are needed.
-
-## Existing binaries
-
-- `bin/jq` — tracks latest jqlang/jq release (bare binary, no archive)
-- `bin/jj` — tracks latest jj-vcs/jj release (tar.gz archive)
+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. Only `macos-aarch64` platform entries are needed. Run `dotslash --help` for the full CLI; `create-url-entry` is the only subcommand these workflows use.
## Adding a new DotSlash binary
@@ -27,7 +22,14 @@ task(:ramekin) do
end
```
-The block receives the resolved tag and returns the asset filename. Ignore the tag when the name is fixed (as above), or interpolate it (e.g. `{ |tag| "jj-#{tag}-aarch64-apple-darwin.tar.gz" }`). 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.
+The block receives the resolved tag and returns the asset filename. Ignore the tag when the name is fixed (as above), or interpolate it (e.g. `{ |tag| "jj-#{tag}-aarch64-apple-darwin.tar.gz" }`). 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 embeds the version (e.g. `cq-0.2.1-aarch64-apple-darwin/cq`), pass `path:` a proc — it receives the resolved tag just like the asset block, so the generated `path` stays correct across release bumps:
+
+```ruby
+desc "Update cq"
+task(:cq) do
+ update_dotslash_release(name: "cq", repo: "technicalpickles/cq", path: ->(tag) { "cq-#{tag.delete_prefix("v")}-aarch64-apple-darwin/cq" }) { |tag| "cq-#{tag.delete_prefix("v")}-aarch64-apple-darwin.tar.gz" }
+end
+```
### 2. Generate and verify
@@ -39,10 +41,10 @@ bin/<name> --help # or whatever smoke test is appropriate
### Hand-writing the file (fallback)
-When the helper doesn't fit — a non-GitHub host or an unusual URL scheme — build the entry by hand:
+Prefer extending `update_dotslash_release` over hand-writing JSON. Reserve hand-writing for cases the helper structurally can't reach — a non-GitHub host or an unusual URL scheme:
1. Find the asset with `gh release view --repo owner/repo --json tagName,assets` and pick the `macos-arm64` / `macos-aarch64` / `darwin-arm64` one.
-2. Run `dotslash -- create-url-entry URL`. It computes size and blake3 hash and infers `format` from the URL suffix; you fill in `path`.
+2. Run `dotslash -- create-url-entry URL`. It computes size and hash and infers `format` from the URL suffix; you fill in `path`.
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 -`).
4. Write `bin/<name>` with the structure below, then `chmod +x`:
@@ -85,25 +87,9 @@ rake dotslash:all # every binary
rake dotslash:<name> # a single binary
```
-This regenerates the DotSlash files in `bin/` with current release artifacts, fetching each tool's latest tag dynamically via `gh release view`.
-
-## DotSlash CLI reference
-
-```bash
-dotslash -- create-url-entry URL # Generate platform entry from a URL
-dotslash -- b3sum FILE # Compute blake3 hash of a local file
-dotslash -- sha256 FILE # Compute sha256 hash of a local file
-dotslash -- fetch DOTSLASH_FILE # Fetch and print cached exe path (don't run)
-dotslash -- parse DOTSLASH_FILE # Parse and validate a dotslash file
-dotslash -- clean # Clear the dotslash cache
-dotslash -- cache-dir # Print cache directory path
-```
-
## Key details
- Use `blake3` (preferred) or `sha256` for the hash. The `create-url-entry` helper defaults to blake3.
-- Providers are tried in order. Put the direct HTTP URL first (faster for public repos), then the GitHub Release provider (works when authenticated).
-- The `format` field must be a recognized value: `tar.gz`, `tar.zst`, `tar.xz`, `tar.bz2`, `tar`, `zip`, `gz`, `zst`, `xz`, `bz2`, or omitted for uncompressed binaries. `tgz` is not valid; use `tar.gz`.
+- 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, which is what the helper interpolates. `+` only means "space" in a query string, not in a path, so GitHub's download endpoint accepts it raw.
-- The cache lives at `~/Library/Caches/dotslash` on macOS.
+- Release tags containing `+` (e.g. `v2026-07-01+564c15b`) work unescaped in the provider URL — GitHub's download endpoint accepts them raw.
diff --git a/bin/cq b/bin/cq
new file mode 100755
index 00000000..b464cf9f
--- /dev/null
+++ b/bin/cq
@@ -0,0 +1,25 @@
+#!/usr/bin/env dotslash
+
+{
+ "name": "cq",
+ "platforms": {
+ "macos-aarch64": {
+ "size": 12682402,
+ "hash": "blake3",
+ "digest": "37a605fd779eed6f7c78b08bb98e2cbb185e332998f3f02e47fa50974b9f0711",
+ "path": "cq-0.2.1-aarch64-apple-darwin/cq",
+ "providers": [
+ {
+ "url": "https://github.com/technicalpickles/cq/releases/download/v0.2.1/cq-0.2.1-aarch64-apple-darwin.tar.gz"
+ },
+ {
+ "type": "github-release",
+ "repo": "https://github.com/technicalpickles/cq",
+ "tag": "v0.2.1",
+ "name": "cq-0.2.1-aarch64-apple-darwin.tar.gz"
+ }
+ ],
+ "format": "tar.gz"
+ }
+ }
+}