diff --git a/.changeset/config.json b/.changeset/config.json index b8213321..a0c0eb35 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,12 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": [] + "ignore": [ + "@taskless/vale-darwin-arm64", + "@taskless/vale-darwin-x64", + "@taskless/vale-linux-arm64", + "@taskless/vale-linux-x64", + "@taskless/vale-win32-arm64", + "@taskless/vale-win32-x64" + ] } diff --git a/.github/scripts/vale-manifest.json b/.github/scripts/vale-manifest.json new file mode 100644 index 00000000..d78e4cd5 --- /dev/null +++ b/.github/scripts/vale-manifest.json @@ -0,0 +1,81 @@ +{ + "comment": [ + "The pinned upstream Vale release and the digests that gate packaging it.", + "This file is the trust boundary for @taskless/vale-* : the publish pipeline", + "verifies every downloaded archive against the sha256 recorded here and", + "refuses to continue on a mismatch, so changing which bytes can be published", + "means changing this file, which means passing code review.", + "", + "Each sha256 is the digest of the RELEASE ARCHIVE, not of the executable", + "inside it. Upstream publishes vale__checksums.txt covering the", + "archives, so these values are independently checkable against upstream, and", + "the archive is verified before anything is unpacked from it.", + "", + "`asset` is a template; {version} is replaced with valeVersion. The detect", + "phase of .github/workflows/vale-binaries.yml rewrites valeVersion and every", + "sha256 from upstream's checksums file, and leaves the templates alone." + ], + "valeVersion": "3.17.1", + "upstream": { + "repository": "errata-ai/vale", + "tag": "v{version}", + "downloadUrl": "https://github.com/errata-ai/vale/releases/download/v{version}/{asset}", + "checksumsAsset": "vale_{version}_checksums.txt" + }, + "platforms": [ + { + "package": "@taskless/vale-darwin-arm64", + "directory": "packages/vale-darwin-arm64", + "os": "darwin", + "cpu": "arm64", + "asset": "vale_{version}_macOS_arm64.tar.gz", + "archiveMember": "vale", + "sha256": "80cacf85ef23f53cfdd77355ec41a6ef99aec136f15dfb3517723482f35593f9" + }, + { + "package": "@taskless/vale-darwin-x64", + "directory": "packages/vale-darwin-x64", + "os": "darwin", + "cpu": "x64", + "asset": "vale_{version}_macOS_64-bit.tar.gz", + "archiveMember": "vale", + "sha256": "b37ab999dfd1414d041bd2e94ced103292d634da76f954c385bf789dc7f5f939" + }, + { + "package": "@taskless/vale-linux-arm64", + "directory": "packages/vale-linux-arm64", + "os": "linux", + "cpu": "arm64", + "asset": "vale_{version}_Linux_arm64.tar.gz", + "archiveMember": "vale", + "sha256": "92d91ebf9ee69ec077379be95cd09e6710ab33d3d5bab66bb482e66ebc80dc23" + }, + { + "package": "@taskless/vale-linux-x64", + "directory": "packages/vale-linux-x64", + "os": "linux", + "cpu": "x64", + "asset": "vale_{version}_Linux_64-bit.tar.gz", + "archiveMember": "vale", + "sha256": "db947f89f2292e6a0381a61de155f6a5f5cb4cb460ca178ea412ef605559cefd" + }, + { + "package": "@taskless/vale-win32-arm64", + "directory": "packages/vale-win32-arm64", + "os": "win32", + "cpu": "arm64", + "asset": "vale_{version}_Windows_arm64.zip", + "archiveMember": "vale.exe", + "sha256": "6fe10e873b09cf31feab2780ac9738ba42d69fb00fac491faadd3cb97c040587" + }, + { + "package": "@taskless/vale-win32-x64", + "directory": "packages/vale-win32-x64", + "os": "win32", + "cpu": "x64", + "asset": "vale_{version}_Windows_64-bit.zip", + "archiveMember": "vale.exe", + "sha256": "0be3fead4e845fc7e740ad8a7e744eee3041ef8c874748a947b7adb63250a642" + } + ] +} diff --git a/CLAUDE.md b/CLAUDE.md index a327b4fb..be7d6f5e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,18 +26,36 @@ When running OpenSpec commands in this repo, use `pnpm openspec` instead of a ba - **ALWAYS** wait for confirmation before committing. After staging changes with `git add`, present a summary and pause for user approval before running the commit. This allows the user to review diffs and catch issues early. +- **CHECK the clone is not shallow before rebasing or force-pushing.** A `git clone --depth=N` also implies `--single-branch`, which leaves the clone unable to do ordinary branch work: + + ```bash + git rev-parse --is-shallow-repository # must be false + git config --get-all remote.origin.fetch # must be +refs/heads/*:refs/remotes/origin/* + ``` + + If either is wrong, repair it once — both are local settings, nothing is committed: + + ```bash + git fetch --unshallow + git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*' + git fetch origin + ``` + + Until then: `--force-with-lease` fails with `stale info` on every branch (there is no remote-tracking ref to lease against, so people fall back to a bare `--force`), `git push -u` cannot store an upstream, `gh pr create` needs an explicit `--head `, and `git branch -r` shows only `main`. The dangerous one is quieter — `git rebase main` is only correct while the merge base sits inside the shallow window, so as `main` advances a rebase can reconstruct the wrong base without saying so. + ## PR Issue References Reference issues as a **trailing line at the bottom of the PR body**, not inline in the opening paragraph: -| Syntax | Effect | -| --------------------- | ---------------------------- | -| `Fixes #1234` | Closes GitHub issue on merge | -| `Fixes TSKL-1234` | Closes Taskless Linear issue | -| `Refs GH-1234` | Links without closing | -| `Refs LINEAR-ABC-123` | Links Linear issue | +| Syntax | Effect | +| --------------------- | ------------------------------- | +| `Fixes #1234` | Closes GitHub issue on merge | +| `Fixes TSKL-1234` | Closes Taskless Linear issue | +| `Fixes OSS-123` | Closes an OSS-team Linear issue | +| `Refs GH-1234` | Links without closing | +| `Refs LINEAR-ABC-123` | Links Linear issue | -- `TSKL-` is the Taskless Linear team prefix; a bare `TSKL-NNNN` resolves without a URL. +- A bare `-NNN` resolves without a URL for **any** Linear team, not just `TSKL-`. `TSKL-` is Product and `OSS-` is the open-source team; verified with `OSS-23`, which the integration linked and moved to In Review on PR creation. - `Fixes` for the issue this PR resolves; `Refs` for a parent or related issue that stays open. - Mentioning an issue in prose (`Found while investigating TSKL-5678.`) is **not** a reference — a PR can cite an issue mid-body with no trailing directive at all. - Only use a reference you can verify from user input, the branch name, commits, PR discussion, or tracker output. Never invent an issue number. diff --git a/openspec/changes/add-vale-binary-packages/.openspec.yaml b/openspec/changes/add-vale-binary-packages/.openspec.yaml new file mode 100644 index 00000000..8e7013b8 --- /dev/null +++ b/openspec/changes/add-vale-binary-packages/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-27 diff --git a/openspec/changes/add-vale-binary-packages/design.md b/openspec/changes/add-vale-binary-packages/design.md new file mode 100644 index 00000000..e84a003e --- /dev/null +++ b/openspec/changes/add-vale-binary-packages/design.md @@ -0,0 +1,150 @@ +## Context + +The CLI shells out to engine binaries. For ast-grep this already works without depending on any install-time step: `findSgBinary()` (`packages/cli/src/rules/scan.ts:38-61`) calls `createRequire(import.meta.url).resolve('@ast-grep/cli-/package.json')`, execs the binary beside it, and falls back to `"sg"` on `PATH`. Its comment records why that indirection exists — `@ast-grep/cli`'s `postinstall` hardlink **breaks under `pnpm dlx`'s strict dependency isolation, leaving a placeholder text file** where the binary should be. + +Verified facts that shape this design: + +- `@ast-grep/cli-darwin-arm64` ships `ast-grep` at mode `-rwxr-xr-x` (46 MB): **the executable bit survives an npm tarball**, so nothing needs to `chmod` at install time. +- Only the host-matching platform package installs — `optionalDependencies` plus `os`/`cpu` filtering (this repo's store contains `@ast-grep+cli-darwin-arm64` and no other). +- Upstream ast-grep still needs its `postinstall` because it exposes `bin: {sg, ast-grep}` for humans; the script only hardlinks the platform binary into the main package so those `bin` entries work. + +There is no equivalent Vale distribution to resolve. The one published package, `@vvago/vale` (maintainer `zeropaper`, not the Vale project), ships an empty package plus a `postinstall` that downloads the release archive. + +`release.yml` is deliberately hardened: a credential-free `version` job consumes untrusted changeset text, a credential-free `check` job decides whether to publish, and only then does a `publish` job exist with OIDC and the `npm-production` environment. Its header states the trust boundary explicitly — "what can merge to main". Any binary fetching must preserve that property. + +## Goals / Non-Goals + +**Goals:** produce per-platform Vale packages the existing resolver pattern can find; require **no lifecycle script** anywhere in the chain; keep the Vale version legible in the package version; make wrong-version resolution impossible rather than merely discouraged; keep the trust boundary at code review. + +**Non-Goals:** the resolution/exec code itself (that is `add-vale-rule-engine`); generating Vale rules; committing binaries to this repository; supporting arbitrary Vale versions simultaneously. + +## Decisions + +### D1 — Packages live in this repo, published to the Taskless scope + +`packages/vale-/`, published alongside `@taskless/cli`. npm scope ownership and the OIDC trusted-publisher binding stay in one place, and the consumer of these packages is in the same workspace. + +- **Alternative — a separate repository:** rejected; it would need its own npm publish identity and trusted-publisher configuration for what is a sibling artifact of the CLI, and version coordination would cross a repo boundary. + +### D2 — ast-grep's packaging, without ast-grep's installation + +Each package contains the Vale binary, a `package.json` declaring `os`/`cpu`, a `README`, and Vale's MIT `LICENSE`. **No `bin`, no code, no scripts.** + +We exec Vale by explicit path, so nothing needs hardlinking into the consuming package — the entire reason upstream ast-grep runs a `postinstall`. Dropping it removes the failure mode this repo already hit under `pnpm dlx`, and means no package-manager script policy can prevent the binary from being present. + +- **Alternative — depend on `@vvago/vale`:** rejected. Its `postinstall` executes during a **consumer's** install, under a policy we cannot set; pnpm 10 blocks dependency build scripts without an `onlyBuiltDependencies` allowlist, producing no binary and no error. The download also carries no npm integrity guarantee and fails behind proxies and offline mirrors. The same objection would apply if the Vale project published it. +- **Alternative — download at runtime into `node_modules`:** rejected. The canonical invocation is `npx @taskless/cli`, so the install is ephemeral and the binary would be re-fetched on essentially every run; writing into `node_modules` is also unsafe where pnpm's content-addressable store is hard-linked into unrelated projects. + +### D3 — Binaries are fetched at release time, never committed + +Seven platforms at roughly 10–20 MB each would live in git history permanently. The package directories are source-only and gitignore the binary; the release pipeline places it before packing. + +**Stated plainly: a published tarball is not reproducible from a plain `git clone`.** Reproducing one requires re-running the fetch against the pinned upstream release. The committed checksums (D6) are what make that reproduction verifiable. + +- **Alternative — commit the binaries:** rejected on permanent repository weight. +- **Alternative — Git LFS:** rejected; adds a hosting dependency and a clone-time requirement for every contributor, to solve a problem the release pipeline already handles. + +### D4 — All-prerelease timestamp versioning + +Every platform package is versioned `-` — e.g. `3.15.2-20260727120000`. **A plain `` is never published.** + +Four properties, each load-bearing: + +1. **Provenance stays legible.** The upstream Vale version is the leading component, readable without opening the package. +2. **No version is ever spent.** A packaging fix against the same upstream Vale is a new timestamp on the same base. Mirroring upstream exactly (`3.15.2` ⇒ Vale 3.15.2) has no such escape hatch, and the obvious one is invalid: `3.15.2-taskless.1` is a _prerelease_ and sorts **before** `3.15.2`. +3. **Exact pinning is enforced by semver, not convention.** A prerelease satisfies a range only when the range names the same `major.minor.patch` with a prerelease, so `^3.15.2` cannot match `3.15.2-20260727120000`. A consumer physically cannot float across versions. +4. **Timestamps order correctly.** `20260727120000` is a valid numeric identifier — no leading zero, ~2×10¹³, inside safe-integer range — so prerelease comparison is numeric and monotonic. + +- **Alternative — independent semver with `valeVersion` as metadata:** rejected; solves (2) but discards (1) and (3). +- **Alternative — mirror upstream exactly:** rejected per (2). + +### D5 — Platform packages bypass the release flow entirely + +Changesets bumps semver by release type and its pre-mode emits counters (`-next.0`), not timestamps; it cannot express D4. The platform packages go in `.changeset/config.json` `ignore`, and a workflow owns their versions. **Two version systems in one repository, deliberately** — changesets for `@taskless/cli`, a timestamp stamp for the platform packages. + +They do not participate in `release.yml` at all. A standalone workflow watches upstream Vale on a schedule and acts in **two phases**, which is what reconciles automation with the reviewed-checksum boundary in D6: + +1. **Detect (unattended).** When upstream publishes a release the repository has not packaged, the workflow opens a pull request updating the pinned Vale version and the committed checksums. It publishes nothing. +2. **Publish (on merge).** Merging that pull request triggers the fetch → verify → stamp → pack → publish run. The checksums it verifies against are the ones just reviewed. + +Nobody has to notice a Vale release, and nothing is published on bytes a human has not signed off on. A single-phase "detect and publish" workflow cannot have both — it would either skip verification or verify against a digest it discovered itself, which verifies nothing. + +Safe to automate because **publishing a platform package changes nothing on its own** — the CLI pins an exact version (D8), so a newly published package is inert until someone bumps that pin. Two independent gates, then: review to publish the package, and a separate deliberate bump to adopt it. + +This also avoids a trap: a freshly stamped timestamp is never already on npm, so any "is this version published?" check would fire on every run. The upstream-version comparison, not a published-version check, is what bounds releases. + +- **Alternative — route these through `release.yml`:** rejected; it is built around changesets and a published-version check, neither of which applies here, and coupling them would mean a Vale release could not ship without a CLI release. + +### D6 — Committed checksums are the trust boundary + +The SHA256 of each platform's upstream release asset is committed to the repository and reviewed like any other change. The publish pipeline verifies every fetched binary against it and refuses a mismatch. + +This preserves what `release.yml`'s header claims: the real perimeter is "what can merge to main". Fetching an unverified third-party binary inside a job holding an OIDC identity would move that perimeter to "whatever the upstream host served today". Verification should also happen in a **credential-free** step, so the credentialed job only ever handles bytes that already matched a reviewed digest. + +### D7 — `release.yml` is left alone; the publishing identity is inherited + +Because the platform packages never route through `release.yml` (D5), its `check` job reading `packages/cli/package.json` stays correct — the CLI remains the only package it publishes. + +Recorded rather than fixed: that check is hardcoded, so any _future_ package that did expect to ride the release flow would be invisible to it. Not a problem this change creates, and not one it needs to solve. + +The publishing security model is inherited, not reinvented. The platform-package workflow authenticates through the repository's existing npm trusted publishing — a short-lived OIDC-minted token bound to the `npm-production` environment, with no stored registry token — and follows the same hardening conventions (SHA-pinned actions, no workflow-wide permission grants, no interpolation of untrusted text into `run:`, `--ignore-scripts` on install). + +That arrangement was established directly on `main` and is **not currently described by any spec** — `openspec/specs/` contains no requirement covering `release.yml`, OIDC, or trusted publishing. This change does not backfill that; it states the dependency so the new workflow is understood to sit inside the existing perimeter rather than beside it. + +### D8 — The CLI pins an exact version by hand + +`packages/cli` declares each platform package in `optionalDependencies` at a literal exact version. Given D4, no range syntax could resolve them anyway; the literal makes the intent explicit and the pin greppable. + +`optionalDependencies` specifically, not `devDependencies`: a devDependency is not installed for anyone consuming `@taskless/cli`, so the binary would reach contributors to this repository and no one else. `optional` is what lets an unsupported platform install the CLI cleanly with no platform package present. + +The pin is **not** `workspace:*`. Under D5 the platform packages are versioned by a workflow on upstream Vale's cadence, so a workspace protocol would silently re-point the CLI at whatever was stamped most recently. A literal version means a newly published platform package is inert until someone deliberately bumps the CLI — which is what makes the automatic publish workflow safe to run unattended. + +- **Alternative — `workspace:*` rewritten at publish:** rejected; it couples the CLI's shipped Vale version to release timing rather than to a reviewed decision. + +## Risks / Trade-offs + +- **Published tarballs are not reproducible from a clone** (D3) → committed checksums make reproduction verifiable; the fetch is pinned to a specific upstream release. +- **The repo becomes a redistributor of a third-party binary** → Vale is MIT; each package ships the upstream `LICENSE` and attribution. Upstream security fixes require us to re-publish, so tracking Vale releases becomes an ongoing obligation rather than a one-time task. +- **Two version systems in one repository** (D5) → confined to the `ignore` list plus one release script; `@taskless/cli` keeps its normal changesets flow untouched. +- **Timestamp versions read as noise** in `npm view` history → accepted; they are machine-pinned and never hand-typed. +- **A platform without a published package silently falls back to `PATH`** → the resolver reports the engine unavailable rather than failing the run (`add-vale-rule-engine` D6), but the degradation is quiet. The architecture matrix should be chosen to cover the realistic deployment surface, not the minimum. +- **Install weight grows** → one platform binary per install, in the range ast-grep's 46 MB already established as acceptable here. + +## Migration Plan + +Purely additive. The packages can be published before anything consumes them; the CLI gains `optionalDependencies` only once they exist. Nothing in the CLI's current behavior changes until `add-vale-rule-engine` adds the resolver, and that change degrades cleanly when no package resolves. + +Rollback is deprecation of a published version plus reverting the CLI's pin — no consumer state to unwind. + +## Resolved Questions + +### The architecture matrix is six packages, not ast-grep's seven + +Vale 3.17.1 publishes exactly six binary assets, and the matrix is those six: + +| Package | os | cpu | Upstream asset | Binary in archive | +| ----------------------------- | -------- | ------- | --------------------------------- | ----------------- | +| `@taskless/vale-darwin-arm64` | `darwin` | `arm64` | `vale_3.17.1_macOS_arm64.tar.gz` | `vale` | +| `@taskless/vale-darwin-x64` | `darwin` | `x64` | `vale_3.17.1_macOS_64-bit.tar.gz` | `vale` | +| `@taskless/vale-linux-arm64` | `linux` | `arm64` | `vale_3.17.1_Linux_arm64.tar.gz` | `vale` | +| `@taskless/vale-linux-x64` | `linux` | `x64` | `vale_3.17.1_Linux_64-bit.tar.gz` | `vale` | +| `@taskless/vale-win32-arm64` | `win32` | `arm64` | `vale_3.17.1_Windows_arm64.zip` | `vale.exe` | +| `@taskless/vale-win32-x64` | `win32` | `x64` | `vale_3.17.1_Windows_64-bit.zip` | `vale.exe` | + +ast-grep's seventh is `win32-ia32`, and Vale publishes no 32-bit Windows asset, so there is nothing to package. Directory names are `packages/vale--/`, matching the package-name suffix. + +**No libc or toolchain suffix in the names** — `vale-linux-x64`, not `vale-linux-x64-gnu`; `vale-win32-x64`, not `-msvc`. ast-grep carries those suffixes because Rust target triples disambiguate several builds per platform. Vale publishes exactly one build per os/cpu pair, so a suffix would disambiguate nothing while asserting a toolchain nobody verified. + +### musl stays on the `PATH` fallback + +Upstream publishes no musl asset, so there is nothing to package for Alpine. That is not a naming detail: Vale's Linux build is **dynamically linked against glibc** on both architectures — verified against the pinned 3.17.1 archives as `ELF 64-bit LSB executable, x86-64, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, stripped` and, for arm64, `ELF 64-bit LSB executable, ARM aarch64, dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, stripped` — so it is not a static Go binary and will not run on musl even if it were installed there. The two differ in interpreter path and minimum kernel, so each linux README quotes its own architecture's string rather than sharing one. A musl host therefore falls back to a `vale` on `PATH`, and the linux packages' READMEs say so plainly rather than leaving a user to discover it as a loader error. + +This matches the existing gap rather than widening it: `findSgBinary()` maps every Linux to `-gnu` today, so Alpine already falls through for ast-grep. + +### Pinned Vale version: 3.17.1, tracked by the detect phase + +3.17.1 (released 2026-08-05) is the pinned version, recorded in `.github/scripts/vale-manifest.json` beside the scripts that consume it. The manifest holds the version once, and per platform the asset-name template, the archive member to unpack, and the SHA256 **of the release archive** — upstream's `vale__checksums.txt` covers the archives rather than the executables inside them, so a committed digest is independently checkable against upstream and the archive is verified before anything is unpacked from it. + +Tracking policy is the detect phase in D5: a weekly schedule opens a pull request whenever upstream is ahead. A Vale **security** release should not wait for that cadence — the same workflow takes a manual `detect` dispatch, and the expectation is that a security release is mirrored within a day rather than at the next scheduled run. + +Which Vale version **the CLI** pins is a separate decision, made when the CLI's `optionalDependencies` land; a published platform package is inert until that pin names it (D8). diff --git a/openspec/changes/add-vale-binary-packages/proposal.md b/openspec/changes/add-vale-binary-packages/proposal.md new file mode 100644 index 00000000..41c787e2 --- /dev/null +++ b/openspec/changes/add-vale-binary-packages/proposal.md @@ -0,0 +1,49 @@ +## Why + +`add-vale-rule-engine` makes Vale a first-class rule engine, and a first-class engine cannot depend on the user having installed a binary themselves. The CLI already solves this for ast-grep — `findSgBinary()` (`packages/cli/src/rules/scan.ts:38-61`) resolves the platform-specific npm package from its own module context and execs the binary beside it — but there is no equivalent npm distribution of Vale to resolve. The only published one, `@vvago/vale`, is third-party and downloads the binary in a `postinstall`, which runs under the **consumer's** package-manager policy; pnpm 10 blocks dependency build scripts by default, so it yields no binary and no error. + +This change produces the artifact the resolver needs: per-platform Vale binary packages, published from this repo, that require no lifecycle script to be usable. + +## What Changes + +- Add `packages/vale-/` workspace packages — one per supported platform — each carrying the Vale binary in its published tarball with `os`/`cpu` declared, and **no `bin`, no code, and no install scripts**. +- Binaries are **not committed to git**. Each package directory holds `package.json`, `README.md`, and `LICENSE` only; CI fetches the binary at release time and publishes a complete tarball. +- Version every platform package as an **all-prerelease timestamp**, `-` (e.g. `3.15.2-20260727120000`). A plain `` is never published. +- Verify each fetched binary against a **committed, reviewed SHA256 checksum**; the publish job refuses a mismatch. +- Add a **standalone workflow** that watches upstream Vale and publishes when it is ahead of what the repository has packaged — independent of `release.yml`, which is left unchanged. +- Exclude the platform packages from changesets (`ignore`), since the workflow owns their versions. +- `packages/cli` declares the platform packages as `optionalDependencies` pinned to literal exact versions, so a newly published package is inert until the pin is deliberately bumped. + +## Capabilities + +### New Capabilities + +- `vale-binary-packages`: Per-platform npm packages carrying the Vale binary — their contents and constraints (`os`/`cpu`, no scripts, no `bin`), the all-prerelease timestamp versioning scheme, checksum verification of fetched binaries, and the dedicated publish workflow. + +### Modified Capabilities + +- `infrastructure`: The changesets configuration excludes the workflow-versioned platform packages, so changesets neither versions nor publishes them. +- `cli`: The CLI declares the Vale platform packages as `optionalDependencies` pinned to literal exact versions, so the host-matching binary installs alongside it. + +## Impact + +- **New**: `packages/vale-/` (one per supported platform), a committed checksum manifest, and a GitHub Actions workflow that fetches, verifies, stamps, packs, and publishes. +- **Modified**: `.changeset/config.json` (`ignore` list), `packages/cli/package.json` (`optionalDependencies`). `.github/workflows/release.yml` is **not** modified — the platform packages do not route through it. +- **Not in scope**: the code that resolves and execs the binary. `findSgBinary()`'s generalization into a shared helper belongs to `add-vale-rule-engine`; this change only guarantees there is something for it to resolve. +- **Published artifacts are not reproducible from a plain `git clone`** — the binary is fetched at release time, not stored in the repository. +- **Supply chain**: the repository becomes a redistributor of a third-party binary. Vale is MIT, so redistribution requires attribution; the committed checksums keep "what can merge to `main`" as the trust boundary. + +## Delivery shape + +**Release impact: minor.** Adds per-platform binary packages and declares them as CLI `optionalDependencies`. Install gains a bundled binary — additive, but a change to what shipping the CLI actually delivers. + +**Stacked, merging forward.** Publishing is inert until something pins it, which is what makes these units independently safe. + +| Unit | Scope | Safe alone because | +| ---- | ------------------------------------------------------------- | ----------------------------------------------------- | +| 1 | Package scaffolding, committed checksums, changesets `ignore` | Nothing published, nothing consumed — repository-only | +| 2 | Fetch, verify, stamp, and the two-phase release workflow | Publishes packages no consumer references yet | +| 3 | The CLI's `optionalDependencies` pin | The packages it pins already exist | + +Unit 3 is the only one with user-visible effect, and it cannot precede unit 2 — reversing that order would pin a version that does not exist. +**Tracking:** OSS-22 diff --git a/openspec/changes/add-vale-binary-packages/specs/cli/spec.md b/openspec/changes/add-vale-binary-packages/specs/cli/spec.md new file mode 100644 index 00000000..b92c6b2e --- /dev/null +++ b/openspec/changes/add-vale-binary-packages/specs/cli/spec.md @@ -0,0 +1,27 @@ +## ADDED Requirements + +### Requirement: The CLI declares Vale platform packages as optional dependencies + +`packages/cli` SHALL declare every supported Vale platform package in `optionalDependencies`, so that installing the CLI also installs the Vale binary matching the host. Each SHALL be pinned to a literal exact version rather than a range or a workspace protocol, so that a newly published platform package reaches the CLI only through a deliberate change. + +The declaration SHALL NOT be a `devDependency`, which would not be installed for consumers of the CLI. `optionalDependencies` is required so an unsupported host installs the CLI successfully with no platform package present. + +#### Scenario: Installing the CLI brings the host's Vale binary + +- **WHEN** the CLI is installed on a supported platform +- **THEN** the matching Vale platform package is installed alongside it and the binary is resolvable from the CLI's module context + +#### Scenario: Unsupported platform still installs + +- **WHEN** the CLI is installed on a platform with no published Vale package +- **THEN** the install succeeds with no platform package present, and no error is raised at install time + +#### Scenario: Versions are pinned exactly + +- **WHEN** the CLI's `optionalDependencies` are inspected in a published tarball +- **THEN** each Vale platform package is pinned to a single exact version, not a range or workspace protocol + +#### Scenario: A newer platform package does not change the CLI + +- **WHEN** a platform package is published for a newer upstream Vale release and the CLI's pin is unchanged +- **THEN** the CLI continues to resolve the pinned version diff --git a/openspec/changes/add-vale-binary-packages/specs/infrastructure/spec.md b/openspec/changes/add-vale-binary-packages/specs/infrastructure/spec.md new file mode 100644 index 00000000..958d4069 --- /dev/null +++ b/openspec/changes/add-vale-binary-packages/specs/infrastructure/spec.md @@ -0,0 +1,20 @@ +## ADDED Requirements + +### Requirement: Script-versioned packages are excluded from changesets + +Workspace packages whose versions are assigned by a release workflow SHALL be listed in the changesets `ignore` configuration, so that changesets neither versions nor publishes them and no changeset is required for them. + +#### Scenario: Changesets does not version the platform packages + +- **WHEN** `changeset version` runs +- **THEN** the workflow-versioned platform packages are left at their current versions + +#### Scenario: Changesets does not publish the platform packages + +- **WHEN** the release flow publishes on the default branch +- **THEN** it publishes only the packages changesets manages, and the platform packages are untouched + +#### Scenario: A platform-package change needs no changeset + +- **WHEN** a pull request modifies only workflow-versioned platform packages +- **THEN** the changeset requirement check does not fail for the absence of a changeset diff --git a/openspec/changes/add-vale-binary-packages/specs/vale-binary-packages/spec.md b/openspec/changes/add-vale-binary-packages/specs/vale-binary-packages/spec.md new file mode 100644 index 00000000..528201da --- /dev/null +++ b/openspec/changes/add-vale-binary-packages/specs/vale-binary-packages/spec.md @@ -0,0 +1,117 @@ +## ADDED Requirements + +### Requirement: A Vale binary package is published per supported platform + +The repository SHALL publish one npm package per supported platform, each carrying the Vale executable for that platform in its published tarball. Each package SHALL declare `os` and `cpu` matching the binary it carries, so that a consumer installs only the package matching its host. + +#### Scenario: Only the host-matching package installs + +- **WHEN** a consumer installs a package that declares every platform package as an optional dependency +- **THEN** only the package matching the host's `os` and `cpu` is installed, and the remainder are skipped without failing the install + +#### Scenario: The binary is executable as published + +- **WHEN** a published platform package is extracted +- **THEN** the Vale executable is present with its executable permission bit intact, requiring no permission change by the consumer + +### Requirement: Platform packages contain no code and no install scripts + +A platform package SHALL contain only the Vale executable and package metadata — `package.json`, a `README`, and the upstream `LICENSE`. It SHALL NOT declare a `bin` entry, SHALL NOT contain executable JavaScript, and SHALL NOT define any lifecycle script. + +Consumers locate the binary by resolving the package and executing it by path, so nothing needs to be linked or copied into place at install time. Availability of the binary SHALL NOT depend on a consumer's package manager permitting dependency lifecycle scripts to run. + +#### Scenario: No lifecycle script is required for the binary to be usable + +- **WHEN** a consumer installs a platform package with dependency lifecycle scripts disabled +- **THEN** the Vale executable is present and usable, because no install-time step was needed to place it + +#### Scenario: Package declares no bin entry + +- **WHEN** a published platform package's `package.json` is inspected +- **THEN** it declares no `bin` entry and no `scripts` entry + +### Requirement: Platform packages are versioned as timestamped prereleases + +Every platform package SHALL be versioned `-`, where `` is the upstream Vale release it carries and the prerelease identifier is the UTC release timestamp. A plain `` SHALL NOT be published. + +#### Scenario: Version names its upstream Vale release + +- **WHEN** a platform package version is read +- **THEN** its `major.minor.patch` component is the upstream Vale version the package carries + +#### Scenario: Republishing the same Vale version is always possible + +- **WHEN** a packaging fix is needed for a Vale version that has already been published +- **THEN** a new package is published with the same `major.minor.patch` and a later timestamp, without requiring the Vale version component to change + +#### Scenario: A caret range cannot resolve a platform package + +- **WHEN** a consumer declares a dependency on a platform package using a caret or tilde range over the Vale version +- **THEN** no published version satisfies it, because every published version is a prerelease + +#### Scenario: Timestamps order monotonically + +- **WHEN** two platform package versions share a Vale version and differ by timestamp +- **THEN** the later timestamp is ordered as the greater version + +### Requirement: Fetched binaries are verified against committed checksums + +The repository SHALL commit a SHA256 checksum for each platform's upstream Vale release asset. The release pipeline SHALL verify every fetched binary against its committed checksum and SHALL refuse to publish on a mismatch. + +Verification SHALL occur before any step holding publish credentials handles the binary, so that a credentialed step only ever processes bytes matching a reviewed digest. + +#### Scenario: Checksum mismatch aborts the release + +- **WHEN** a fetched binary's SHA256 does not match the committed checksum for that platform +- **THEN** the release fails and nothing is published + +#### Scenario: Changing an expected binary requires review + +- **WHEN** the Vale version or a platform's release asset changes +- **THEN** the corresponding committed checksum must change in the repository, passing through code review before any publish can succeed + +### Requirement: Binaries are absent from version control + +The repository SHALL NOT store Vale executables in version control. A platform package directory SHALL contain only its source-controlled metadata, and the executable SHALL be placed into the package by the release pipeline before packing. + +#### Scenario: A clean checkout contains no binaries + +- **WHEN** the repository is cloned +- **THEN** no Vale executable is present in any platform package directory, and the working tree is clean + +### Requirement: Platform packages are released by their own workflow, tracking upstream + +Platform packages SHALL be versioned and published by a workflow dedicated to them, independent of the workflow that releases packages managed by changesets. That workflow SHALL compare the latest upstream Vale release against what the repository has already published, and SHALL publish only when upstream is ahead. + +A published-version check cannot bound these runs — every run stamps a previously unused timestamp — so the upstream comparison SHALL be what prevents redundant publishing. + +#### Scenario: Upstream unchanged publishes nothing + +- **WHEN** the workflow runs and the latest upstream Vale release is already published as a platform package +- **THEN** no package is versioned or published + +#### Scenario: A new upstream release opens a pull request rather than publishing + +- **WHEN** the workflow runs and upstream Vale is ahead of what the repository has published +- **THEN** it opens a pull request updating the pinned Vale version and the committed checksums, and publishes nothing + +#### Scenario: Merging the update publishes the set + +- **WHEN** that pull request is merged +- **THEN** every supported platform package is stamped with the same version and published together, verified against the checksums that were just reviewed + +#### Scenario: Ordinary pushes do not publish platform packages + +- **WHEN** a commit is pushed to the default branch +- **THEN** the changeset-managed release flow publishes no platform package + +### Requirement: Publishing a platform package changes no consumer + +Publishing a platform package SHALL NOT alter the behavior of any already-published consumer. A consumer SHALL reach a newly published platform package only by a deliberate, reviewed change to the version it pins. + +This is what allows the release workflow to run unattended. + +#### Scenario: A new platform package is inert until pinned + +- **WHEN** a platform package is published for a newer upstream Vale release +- **THEN** consumers continue to resolve the version they pin, and none resolves the new package until its pin is changed diff --git a/openspec/changes/add-vale-binary-packages/tasks.md b/openspec/changes/add-vale-binary-packages/tasks.md new file mode 100644 index 00000000..ddf2f9f0 --- /dev/null +++ b/openspec/changes/add-vale-binary-packages/tasks.md @@ -0,0 +1,45 @@ +## 1. Decide the distribution surface + +- [x] 1.1 Enumerate Vale's published release assets for the pinned version and map them to npm `os`/`cpu` pairs; record the resulting architecture matrix (ast-grep's seven — win32 `arm64`/`ia32`/`x64`, darwin `arm64`/`x64`, linux `arm64-gnu`/`x64-gnu` — is the starting point, not the answer) +- [x] 1.2 Decide musl/Alpine: publish a musl variant or leave it on the `PATH` fallback. Note `findSgBinary()` maps every Linux to `-gnu` today, so ast-grep already falls through there +- [x] 1.3 Pin the Vale version the CLI expects, and write down the policy for tracking upstream releases (including expected turnaround for a Vale security release) + +## 2. Package scaffolding + +- [x] 2.1 Create `packages/vale-/` for each platform in the matrix: `package.json` with `name`, `version`, `os`, `cpu`, `files`, and `description` — **no `bin`, no `scripts`, no code** +- [x] 2.2 Add each package's `README.md` and the upstream Vale `LICENSE` (MIT) with attribution +- [x] 2.3 Gitignore the binary inside each package directory so a clean checkout stays binary-free +- [x] 2.4 Add the platform packages to `.changeset/config.json` `ignore` +- [x] 2.5 Confirm `pnpm-workspace.yaml`'s `packages/*` glob picks them up and `pnpm install` succeeds with the binaries absent + +## 3. Checksums and fetch + +- [x] 3.1 Commit a checksum manifest recording the SHA256 of each platform's upstream release asset for the pinned Vale version +- [ ] 3.2 Write the fetch step: download each platform asset, verify against the committed checksum, fail loudly on mismatch, and unpack the executable into its package directory preserving the executable bit +- [ ] 3.3 Ensure verification runs in a credential-free step, so no credentialed step handles unverified bytes +- [ ] 3.4 Tests: a mismatched checksum aborts and publishes nothing; a matching one yields an executable file in the expected location + +## 4. Version stamping + +- [ ] 4.1 Write the stamping step: set every platform package to `-` (UTC), identically across the set +- [ ] 4.2 Assert the stamped version parses as a valid semver prerelease, that the timestamp is a numeric identifier with no leading zero, and that a plain `` is never produced +- [ ] 4.3 Tests: two runs produce ordered versions; the whole set shares one version; a caret range over the Vale version matches nothing + +## 5. Release workflow + +- [ ] 5.1 Add a standalone workflow in two phases, no coupling to `release.yml`: **detect** — on a schedule, compare upstream Vale against what is published and open a PR updating the pinned version + checksums, publishing nothing; **publish** — on merge of that PR, run fetch → verify → stamp → pack → publish against the reviewed checksums +- [ ] 5.2 Bound runs by the upstream comparison, not a published-version check — a fresh timestamp is never already on npm, so that check can never suppress a run +- [ ] 5.3 Follow the existing hardening conventions: SHA-pinned action refs, no workflow-wide permission grants, no `${{ }}` interpolation of untrusted text into `run:`, OIDC trusted publishing bound to the `npm-production` environment, `--ignore-scripts` on install +- [ ] 5.4 Verify an ordinary push to `main` publishes no platform package, and that a run with upstream unchanged publishes nothing + +## 6. CLI wiring + +- [ ] 6.1 Add the platform packages to `packages/cli` `optionalDependencies` at literal exact versions — not `devDependencies` (never installed for consumers) and not `workspace:*` (would silently re-point at the newest stamp) +- [ ] 6.2 Verify installing the CLI on a supported platform yields a resolvable binary from the CLI's module context, and that an unsupported platform installs cleanly with none present +- [ ] 6.3 Confirm publishing a newer platform package leaves an unchanged CLI resolving its pinned version + +## 7. Quality gates + +- [ ] 7.1 `pnpm typecheck && pnpm lint && pnpm test` clean at the repo root +- [ ] 7.2 Dry-run the release workflow end to end without publishing, and confirm the packed tarball contains the executable with its permission bit +- [ ] 7.3 Once published, remove tasks 5.1b–5.1e from `add-vale-rule-engine`, which reduces to the runtime resolution (its task 5.1) alone diff --git a/packages/vale-darwin-arm64/.gitignore b/packages/vale-darwin-arm64/.gitignore new file mode 100644 index 00000000..e58c5eed --- /dev/null +++ b/packages/vale-darwin-arm64/.gitignore @@ -0,0 +1,7 @@ +# The Vale executable is fetched and unpacked by the release pipeline +# (.github/workflows/vale-binaries.yml), never committed. A clean checkout of +# this repository holds no binaries. +vale + +# npm pack output from a local dry run of that pipeline. +*.tgz diff --git a/packages/vale-darwin-arm64/LICENSE b/packages/vale-darwin-arm64/LICENSE new file mode 100644 index 00000000..8ed286bf --- /dev/null +++ b/packages/vale-darwin-arm64/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Joseph Kato + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/vale-darwin-arm64/README.md b/packages/vale-darwin-arm64/README.md new file mode 100644 index 00000000..dfc6d37d --- /dev/null +++ b/packages/vale-darwin-arm64/README.md @@ -0,0 +1,48 @@ +# @taskless/vale-darwin-arm64 + +The [Vale](https://vale.sh) executable for macOS on Apple silicon, redistributed as an npm +package so that `@taskless/cli` can resolve and run it with no download step at +install time. + +## What the published tarball holds + +`vale`, this README, Vale's upstream `LICENSE`, and `package.json`. +Nothing else: no `bin` entry, no JavaScript, and no lifecycle script. A consumer +locates the executable by resolving this package and running the file by path, +so the binary is usable even where the consuming package manager refuses to run +dependency build scripts — which pnpm 10 does by default. + +`os` and `cpu` are declared, so this package installs only on a matching host +and is skipped everywhere else without failing the install. + +## Versioning + +Published versions are `-`: the upstream Vale +release this package carries, then the UTC timestamp of the publishing run. +`3.17.1-20260806120000` carries Vale 3.17.1. A plain `3.17.1` is never +published. + +Every published version is therefore a semver prerelease, which is the point: a +caret or tilde range over the Vale version matches no published version, so a +consumer has to name one exact version and cannot float across releases. + +The `version` field in the repository is the placeholder `0.0.0`. The release +workflow overwrites it with the stamped version immediately before packing, and +the placeholder is never itself published. + +## Where the binary comes from + +The binary is not stored in this repository. `.github/workflows/vale-binaries.yml` +downloads `vale__macOS_arm64.tar.gz` from the upstream +release, checks the archive's SHA256 against the digest committed in +`.github/scripts/vale-manifest.json`, refuses to go further on a mismatch, and +only then unpacks `vale` into the package. Reproducing a published +tarball means re-running that fetch against the same upstream release; the +committed digest is what makes the result independently checkable, since it is +the same digest upstream publishes in `vale__checksums.txt`. + +## License + +Vale is MIT licensed, Copyright (c) 2016 Joseph Kato (errata-ai). The upstream +`LICENSE` ships in this package unmodified. This package redistributes that +build and is not affiliated with or endorsed by the Vale project. diff --git a/packages/vale-darwin-arm64/package.json b/packages/vale-darwin-arm64/package.json new file mode 100644 index 00000000..f4028b3f --- /dev/null +++ b/packages/vale-darwin-arm64/package.json @@ -0,0 +1,23 @@ +{ + "name": "@taskless/vale-darwin-arm64", + "description": "The Vale executable for macOS on Apple silicon, packaged for @taskless/cli", + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/taskless/skills.git", + "directory": "packages/vale-darwin-arm64" + }, + "files": [ + "vale" + ], + "publishConfig": { + "access": "public" + }, + "cpu": [ + "arm64" + ], + "os": [ + "darwin" + ] +} diff --git a/packages/vale-darwin-x64/.gitignore b/packages/vale-darwin-x64/.gitignore new file mode 100644 index 00000000..e58c5eed --- /dev/null +++ b/packages/vale-darwin-x64/.gitignore @@ -0,0 +1,7 @@ +# The Vale executable is fetched and unpacked by the release pipeline +# (.github/workflows/vale-binaries.yml), never committed. A clean checkout of +# this repository holds no binaries. +vale + +# npm pack output from a local dry run of that pipeline. +*.tgz diff --git a/packages/vale-darwin-x64/LICENSE b/packages/vale-darwin-x64/LICENSE new file mode 100644 index 00000000..8ed286bf --- /dev/null +++ b/packages/vale-darwin-x64/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Joseph Kato + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/vale-darwin-x64/README.md b/packages/vale-darwin-x64/README.md new file mode 100644 index 00000000..c8f91643 --- /dev/null +++ b/packages/vale-darwin-x64/README.md @@ -0,0 +1,48 @@ +# @taskless/vale-darwin-x64 + +The [Vale](https://vale.sh) executable for macOS on Intel, redistributed as an npm +package so that `@taskless/cli` can resolve and run it with no download step at +install time. + +## What the published tarball holds + +`vale`, this README, Vale's upstream `LICENSE`, and `package.json`. +Nothing else: no `bin` entry, no JavaScript, and no lifecycle script. A consumer +locates the executable by resolving this package and running the file by path, +so the binary is usable even where the consuming package manager refuses to run +dependency build scripts — which pnpm 10 does by default. + +`os` and `cpu` are declared, so this package installs only on a matching host +and is skipped everywhere else without failing the install. + +## Versioning + +Published versions are `-`: the upstream Vale +release this package carries, then the UTC timestamp of the publishing run. +`3.17.1-20260806120000` carries Vale 3.17.1. A plain `3.17.1` is never +published. + +Every published version is therefore a semver prerelease, which is the point: a +caret or tilde range over the Vale version matches no published version, so a +consumer has to name one exact version and cannot float across releases. + +The `version` field in the repository is the placeholder `0.0.0`. The release +workflow overwrites it with the stamped version immediately before packing, and +the placeholder is never itself published. + +## Where the binary comes from + +The binary is not stored in this repository. `.github/workflows/vale-binaries.yml` +downloads `vale__macOS_64-bit.tar.gz` from the upstream +release, checks the archive's SHA256 against the digest committed in +`.github/scripts/vale-manifest.json`, refuses to go further on a mismatch, and +only then unpacks `vale` into the package. Reproducing a published +tarball means re-running that fetch against the same upstream release; the +committed digest is what makes the result independently checkable, since it is +the same digest upstream publishes in `vale__checksums.txt`. + +## License + +Vale is MIT licensed, Copyright (c) 2016 Joseph Kato (errata-ai). The upstream +`LICENSE` ships in this package unmodified. This package redistributes that +build and is not affiliated with or endorsed by the Vale project. diff --git a/packages/vale-darwin-x64/package.json b/packages/vale-darwin-x64/package.json new file mode 100644 index 00000000..27c567b1 --- /dev/null +++ b/packages/vale-darwin-x64/package.json @@ -0,0 +1,23 @@ +{ + "name": "@taskless/vale-darwin-x64", + "description": "The Vale executable for macOS on Intel, packaged for @taskless/cli", + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/taskless/skills.git", + "directory": "packages/vale-darwin-x64" + }, + "files": [ + "vale" + ], + "publishConfig": { + "access": "public" + }, + "cpu": [ + "x64" + ], + "os": [ + "darwin" + ] +} diff --git a/packages/vale-linux-arm64/.gitignore b/packages/vale-linux-arm64/.gitignore new file mode 100644 index 00000000..e58c5eed --- /dev/null +++ b/packages/vale-linux-arm64/.gitignore @@ -0,0 +1,7 @@ +# The Vale executable is fetched and unpacked by the release pipeline +# (.github/workflows/vale-binaries.yml), never committed. A clean checkout of +# this repository holds no binaries. +vale + +# npm pack output from a local dry run of that pipeline. +*.tgz diff --git a/packages/vale-linux-arm64/LICENSE b/packages/vale-linux-arm64/LICENSE new file mode 100644 index 00000000..8ed286bf --- /dev/null +++ b/packages/vale-linux-arm64/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Joseph Kato + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/vale-linux-arm64/README.md b/packages/vale-linux-arm64/README.md new file mode 100644 index 00000000..a182d209 --- /dev/null +++ b/packages/vale-linux-arm64/README.md @@ -0,0 +1,57 @@ +# @taskless/vale-linux-arm64 + +The [Vale](https://vale.sh) executable for 64-bit ARM Linux, redistributed as an npm +package so that `@taskless/cli` can resolve and run it with no download step at +install time. + +## What the published tarball holds + +`vale`, this README, Vale's upstream `LICENSE`, and `package.json`. +Nothing else: no `bin` entry, no JavaScript, and no lifecycle script. A consumer +locates the executable by resolving this package and running the file by path, +so the binary is usable even where the consuming package manager refuses to run +dependency build scripts — which pnpm 10 does by default. + +`os` and `cpu` are declared, so this package installs only on a matching host +and is skipped everywhere else without failing the install. + +## Versioning + +Published versions are `-`: the upstream Vale +release this package carries, then the UTC timestamp of the publishing run. +`3.17.1-20260806120000` carries Vale 3.17.1. A plain `3.17.1` is never +published. + +Every published version is therefore a semver prerelease, which is the point: a +caret or tilde range over the Vale version matches no published version, so a +consumer has to name one exact version and cannot float across releases. + +The `version` field in the repository is the placeholder `0.0.0`. The release +workflow overwrites it with the stamped version immediately before packing, and +the placeholder is never itself published. + +## Where the binary comes from + +The binary is not stored in this repository. `.github/workflows/vale-binaries.yml` +downloads `vale__Linux_arm64.tar.gz` from the upstream +release, checks the archive's SHA256 against the digest committed in +`.github/scripts/vale-manifest.json`, refuses to go further on a mismatch, and +only then unpacks `vale` into the package. Reproducing a published +tarball means re-running that fetch against the same upstream release; the +committed digest is what makes the result independently checkable, since it is +the same digest upstream publishes in `vale__checksums.txt`. + +## glibc, and why there is no musl package + +Vale's Linux build is dynamically linked against glibc — `ELF 64-bit LSB +executable, ARM aarch64, dynamically linked, interpreter +/lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0` — so it is not a static Go +binary and it does not run on musl-based distributions such as Alpine. Upstream +publishes no musl asset, so there is nothing to package for those hosts; they +fall back to a `vale` found on `PATH`. + +## License + +Vale is MIT licensed, Copyright (c) 2016 Joseph Kato (errata-ai). The upstream +`LICENSE` ships in this package unmodified. This package redistributes that +build and is not affiliated with or endorsed by the Vale project. diff --git a/packages/vale-linux-arm64/package.json b/packages/vale-linux-arm64/package.json new file mode 100644 index 00000000..97c852af --- /dev/null +++ b/packages/vale-linux-arm64/package.json @@ -0,0 +1,23 @@ +{ + "name": "@taskless/vale-linux-arm64", + "description": "The Vale executable for 64-bit ARM Linux, packaged for @taskless/cli", + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/taskless/skills.git", + "directory": "packages/vale-linux-arm64" + }, + "files": [ + "vale" + ], + "publishConfig": { + "access": "public" + }, + "cpu": [ + "arm64" + ], + "os": [ + "linux" + ] +} diff --git a/packages/vale-linux-x64/.gitignore b/packages/vale-linux-x64/.gitignore new file mode 100644 index 00000000..e58c5eed --- /dev/null +++ b/packages/vale-linux-x64/.gitignore @@ -0,0 +1,7 @@ +# The Vale executable is fetched and unpacked by the release pipeline +# (.github/workflows/vale-binaries.yml), never committed. A clean checkout of +# this repository holds no binaries. +vale + +# npm pack output from a local dry run of that pipeline. +*.tgz diff --git a/packages/vale-linux-x64/LICENSE b/packages/vale-linux-x64/LICENSE new file mode 100644 index 00000000..8ed286bf --- /dev/null +++ b/packages/vale-linux-x64/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Joseph Kato + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/vale-linux-x64/README.md b/packages/vale-linux-x64/README.md new file mode 100644 index 00000000..6ac78447 --- /dev/null +++ b/packages/vale-linux-x64/README.md @@ -0,0 +1,57 @@ +# @taskless/vale-linux-x64 + +The [Vale](https://vale.sh) executable for x86-64 Linux, redistributed as an npm +package so that `@taskless/cli` can resolve and run it with no download step at +install time. + +## What the published tarball holds + +`vale`, this README, Vale's upstream `LICENSE`, and `package.json`. +Nothing else: no `bin` entry, no JavaScript, and no lifecycle script. A consumer +locates the executable by resolving this package and running the file by path, +so the binary is usable even where the consuming package manager refuses to run +dependency build scripts — which pnpm 10 does by default. + +`os` and `cpu` are declared, so this package installs only on a matching host +and is skipped everywhere else without failing the install. + +## Versioning + +Published versions are `-`: the upstream Vale +release this package carries, then the UTC timestamp of the publishing run. +`3.17.1-20260806120000` carries Vale 3.17.1. A plain `3.17.1` is never +published. + +Every published version is therefore a semver prerelease, which is the point: a +caret or tilde range over the Vale version matches no published version, so a +consumer has to name one exact version and cannot float across releases. + +The `version` field in the repository is the placeholder `0.0.0`. The release +workflow overwrites it with the stamped version immediately before packing, and +the placeholder is never itself published. + +## Where the binary comes from + +The binary is not stored in this repository. `.github/workflows/vale-binaries.yml` +downloads `vale__Linux_64-bit.tar.gz` from the upstream +release, checks the archive's SHA256 against the digest committed in +`.github/scripts/vale-manifest.json`, refuses to go further on a mismatch, and +only then unpacks `vale` into the package. Reproducing a published +tarball means re-running that fetch against the same upstream release; the +committed digest is what makes the result independently checkable, since it is +the same digest upstream publishes in `vale__checksums.txt`. + +## glibc, and why there is no musl package + +Vale's Linux build is dynamically linked against glibc — `ELF 64-bit LSB +executable, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for +GNU/Linux 3.2.0` — so it is not a static Go binary and it does not run on +musl-based distributions such as Alpine. Upstream publishes no musl asset, so +there is nothing to package for those hosts; they fall back to a `vale` found +on `PATH`. + +## License + +Vale is MIT licensed, Copyright (c) 2016 Joseph Kato (errata-ai). The upstream +`LICENSE` ships in this package unmodified. This package redistributes that +build and is not affiliated with or endorsed by the Vale project. diff --git a/packages/vale-linux-x64/package.json b/packages/vale-linux-x64/package.json new file mode 100644 index 00000000..e8da8f8c --- /dev/null +++ b/packages/vale-linux-x64/package.json @@ -0,0 +1,23 @@ +{ + "name": "@taskless/vale-linux-x64", + "description": "The Vale executable for x86-64 Linux, packaged for @taskless/cli", + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/taskless/skills.git", + "directory": "packages/vale-linux-x64" + }, + "files": [ + "vale" + ], + "publishConfig": { + "access": "public" + }, + "cpu": [ + "x64" + ], + "os": [ + "linux" + ] +} diff --git a/packages/vale-win32-arm64/.gitignore b/packages/vale-win32-arm64/.gitignore new file mode 100644 index 00000000..62626c29 --- /dev/null +++ b/packages/vale-win32-arm64/.gitignore @@ -0,0 +1,7 @@ +# The Vale executable is fetched and unpacked by the release pipeline +# (.github/workflows/vale-binaries.yml), never committed. A clean checkout of +# this repository holds no binaries. +vale.exe + +# npm pack output from a local dry run of that pipeline. +*.tgz diff --git a/packages/vale-win32-arm64/LICENSE b/packages/vale-win32-arm64/LICENSE new file mode 100644 index 00000000..8ed286bf --- /dev/null +++ b/packages/vale-win32-arm64/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Joseph Kato + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/vale-win32-arm64/README.md b/packages/vale-win32-arm64/README.md new file mode 100644 index 00000000..898d0c54 --- /dev/null +++ b/packages/vale-win32-arm64/README.md @@ -0,0 +1,48 @@ +# @taskless/vale-win32-arm64 + +The [Vale](https://vale.sh) executable for 64-bit ARM Windows, redistributed as an npm +package so that `@taskless/cli` can resolve and run it with no download step at +install time. + +## What the published tarball holds + +`vale.exe`, this README, Vale's upstream `LICENSE`, and `package.json`. +Nothing else: no `bin` entry, no JavaScript, and no lifecycle script. A consumer +locates the executable by resolving this package and running the file by path, +so the binary is usable even where the consuming package manager refuses to run +dependency build scripts — which pnpm 10 does by default. + +`os` and `cpu` are declared, so this package installs only on a matching host +and is skipped everywhere else without failing the install. + +## Versioning + +Published versions are `-`: the upstream Vale +release this package carries, then the UTC timestamp of the publishing run. +`3.17.1-20260806120000` carries Vale 3.17.1. A plain `3.17.1` is never +published. + +Every published version is therefore a semver prerelease, which is the point: a +caret or tilde range over the Vale version matches no published version, so a +consumer has to name one exact version and cannot float across releases. + +The `version` field in the repository is the placeholder `0.0.0`. The release +workflow overwrites it with the stamped version immediately before packing, and +the placeholder is never itself published. + +## Where the binary comes from + +The binary is not stored in this repository. `.github/workflows/vale-binaries.yml` +downloads `vale__Windows_arm64.zip` from the upstream +release, checks the archive's SHA256 against the digest committed in +`.github/scripts/vale-manifest.json`, refuses to go further on a mismatch, and +only then unpacks `vale.exe` into the package. Reproducing a published +tarball means re-running that fetch against the same upstream release; the +committed digest is what makes the result independently checkable, since it is +the same digest upstream publishes in `vale__checksums.txt`. + +## License + +Vale is MIT licensed, Copyright (c) 2016 Joseph Kato (errata-ai). The upstream +`LICENSE` ships in this package unmodified. This package redistributes that +build and is not affiliated with or endorsed by the Vale project. diff --git a/packages/vale-win32-arm64/package.json b/packages/vale-win32-arm64/package.json new file mode 100644 index 00000000..f98b2c73 --- /dev/null +++ b/packages/vale-win32-arm64/package.json @@ -0,0 +1,23 @@ +{ + "name": "@taskless/vale-win32-arm64", + "description": "The Vale executable for 64-bit ARM Windows, packaged for @taskless/cli", + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/taskless/skills.git", + "directory": "packages/vale-win32-arm64" + }, + "files": [ + "vale.exe" + ], + "publishConfig": { + "access": "public" + }, + "cpu": [ + "arm64" + ], + "os": [ + "win32" + ] +} diff --git a/packages/vale-win32-x64/.gitignore b/packages/vale-win32-x64/.gitignore new file mode 100644 index 00000000..62626c29 --- /dev/null +++ b/packages/vale-win32-x64/.gitignore @@ -0,0 +1,7 @@ +# The Vale executable is fetched and unpacked by the release pipeline +# (.github/workflows/vale-binaries.yml), never committed. A clean checkout of +# this repository holds no binaries. +vale.exe + +# npm pack output from a local dry run of that pipeline. +*.tgz diff --git a/packages/vale-win32-x64/LICENSE b/packages/vale-win32-x64/LICENSE new file mode 100644 index 00000000..8ed286bf --- /dev/null +++ b/packages/vale-win32-x64/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Joseph Kato + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/vale-win32-x64/README.md b/packages/vale-win32-x64/README.md new file mode 100644 index 00000000..6f991f34 --- /dev/null +++ b/packages/vale-win32-x64/README.md @@ -0,0 +1,48 @@ +# @taskless/vale-win32-x64 + +The [Vale](https://vale.sh) executable for x86-64 Windows, redistributed as an npm +package so that `@taskless/cli` can resolve and run it with no download step at +install time. + +## What the published tarball holds + +`vale.exe`, this README, Vale's upstream `LICENSE`, and `package.json`. +Nothing else: no `bin` entry, no JavaScript, and no lifecycle script. A consumer +locates the executable by resolving this package and running the file by path, +so the binary is usable even where the consuming package manager refuses to run +dependency build scripts — which pnpm 10 does by default. + +`os` and `cpu` are declared, so this package installs only on a matching host +and is skipped everywhere else without failing the install. + +## Versioning + +Published versions are `-`: the upstream Vale +release this package carries, then the UTC timestamp of the publishing run. +`3.17.1-20260806120000` carries Vale 3.17.1. A plain `3.17.1` is never +published. + +Every published version is therefore a semver prerelease, which is the point: a +caret or tilde range over the Vale version matches no published version, so a +consumer has to name one exact version and cannot float across releases. + +The `version` field in the repository is the placeholder `0.0.0`. The release +workflow overwrites it with the stamped version immediately before packing, and +the placeholder is never itself published. + +## Where the binary comes from + +The binary is not stored in this repository. `.github/workflows/vale-binaries.yml` +downloads `vale__Windows_64-bit.zip` from the upstream +release, checks the archive's SHA256 against the digest committed in +`.github/scripts/vale-manifest.json`, refuses to go further on a mismatch, and +only then unpacks `vale.exe` into the package. Reproducing a published +tarball means re-running that fetch against the same upstream release; the +committed digest is what makes the result independently checkable, since it is +the same digest upstream publishes in `vale__checksums.txt`. + +## License + +Vale is MIT licensed, Copyright (c) 2016 Joseph Kato (errata-ai). The upstream +`LICENSE` ships in this package unmodified. This package redistributes that +build and is not affiliated with or endorsed by the Vale project. diff --git a/packages/vale-win32-x64/package.json b/packages/vale-win32-x64/package.json new file mode 100644 index 00000000..704336be --- /dev/null +++ b/packages/vale-win32-x64/package.json @@ -0,0 +1,23 @@ +{ + "name": "@taskless/vale-win32-x64", + "description": "The Vale executable for x86-64 Windows, packaged for @taskless/cli", + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/taskless/skills.git", + "directory": "packages/vale-win32-x64" + }, + "files": [ + "vale.exe" + ], + "publishConfig": { + "access": "public" + }, + "cpu": [ + "x64" + ], + "os": [ + "win32" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 597c17d3..fdf5e6c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,6 +152,18 @@ importers: specifier: 0.41.0 version: 0.41.0 + packages/vale-darwin-arm64: {} + + packages/vale-darwin-x64: {} + + packages/vale-linux-arm64: {} + + packages/vale-linux-x64: {} + + packages/vale-win32-arm64: {} + + packages/vale-win32-x64: {} + packages: '@ast-grep/cli-darwin-arm64@0.41.0':