Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions specter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Unreleased changes accumulate under `## Unreleased`. Every user-visible change a

## Unreleased

### Fixed

- The VS Code extension now preserves independently installed Specter CLIs, including newer supported versions. Update the extension to receive the fix. Existing CLI installations need no reinstall. If you have no shell CLI, run Specter: Add CLI to Shell PATH.

---

## v0.15.1 - 2026-09-13
Expand Down
2 changes: 1 addition & 1 deletion specter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Specter validates the artifacts in front of it, not the order you wrote them in.

### VS Code extension (recommended for most users)

Search **Specter SDD** in the Extensions panel. The extension auto-downloads the CLI binary matching the host's OS and architecture, installs it under `~/.specter/bin/`, and wires up the integrated terminal so `specter` works without further setup. To call `specter` from external terminals, run **Specter: Add CLI to Shell PATH** from the command palette once.
Search **Specter SDD** in the Extensions panel. The extension uses a CLI already on your PATH when its version is one the extension supports, and otherwise downloads its own copy under `~/.specter/cli/`. It never replaces a CLI you installed. To call `specter` from a terminal, run **Specter: Add CLI to Shell PATH** from the command palette once; it puts a copy at `~/.specter/bin/specter` when nothing is there.

### CLI, Linux / macOS (tar.gz)

Expand Down
2 changes: 1 addition & 1 deletion specter/docs/CLI_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Specter is a spec compiler toolchain, "a type system for specs." It validates, l

## Installation

Install the VS Code extension for the smoothest path: it auto-downloads the CLI and sets PATH. For CLI-only installs (tar.gz, `.deb`, `.rpm`, Windows zip, or build from source), see the [Install section in the Specter README](../README.md#install). Asset naming pattern: `specter_<version>_<os>_<arch>.<ext>` with lowercase `linux`/`darwin`/`windows` and `amd64`/`arm64`.
Install the VS Code extension for the smoothest path: it uses a supported CLI already on PATH, or downloads its own copy, and can set PATH for you. For CLI-only installs (tar.gz, `.deb`, `.rpm`, Windows zip, or build from source), see the [Install section in the Specter README](../README.md#install). Asset naming pattern: `specter_<version>_<os>_<arch>.<ext>` with lowercase `linux`/`darwin`/`windows` and `amd64`/`arm64`.

---

Expand Down
14 changes: 7 additions & 7 deletions specter/docs/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Get from zero to a working spec pipeline in under 5 minutes.

## 1. Install

**Fastest path — VS Code extension:** search `Specter SDD` in the Extensions panel, install, then run **Specter: Add CLI to Shell PATH** from the command palette once. The extension auto-downloads the CLI binary for your OS and architecture.
**Fastest path, the VS Code extension:** search `Specter SDD` in the Extensions panel, install, then run **Specter: Add CLI to Shell PATH** from the command palette once. The extension uses a CLI already on your PATH when its version is one it supports, and otherwise downloads its own copy; the shell PATH command puts a copy at `~/.specter/bin/specter` for your terminal.

**CLI-only, macOS / Linux:**
```bash
Expand All @@ -32,7 +32,7 @@ For `.deb`, `.rpm`, and other install methods see the [Specter README](../README

## 2. Bootstrap specs from your code

Point Specter at your source directory — it generates draft specs automatically:
Point Specter at your source directory, and it generates draft specs automatically:

```bash
specter reverse src/ # TypeScript / JavaScript
Expand All @@ -54,7 +54,7 @@ This creates a `specs/` directory with one `.spec.yaml` per file group.
specter init
```

Creates `specter.yaml` — the manifest that tells Specter (and the VS Code extension) where your specs and tests live.
Creates `specter.yaml`, the manifest that tells Specter (and the VS Code extension) where your specs and tests live.

---

Expand Down Expand Up @@ -163,7 +163,7 @@ Add this to CI and you're protected.

## What's next?

- **[Getting Started](GETTING_STARTED.md)** — full walkthrough from zero specs to 100% coverage, with AI prompts for every step and VS Code workspace guide
- **[CLI Reference](CLI_REFERENCE.md)** — every command and flag
- **[AI Prompts](AI_PROMPTS.md)** — ready-to-use prompts for the full SDD loop
- **[FAQ](FAQ.md)** — "Do I need to migrate my existing specs?"
- **[Getting Started](GETTING_STARTED.md)**: full walkthrough from zero specs to 100% coverage, with AI prompts for every step and VS Code workspace guide
- **[CLI Reference](CLI_REFERENCE.md)**: every command and flag
- **[AI Prompts](AI_PROMPTS.md)**: ready-to-use prompts for the full SDD loop
- **[FAQ](FAQ.md)**: "Do I need to migrate my existing specs?"
64 changes: 61 additions & 3 deletions specter/specs/spec-vscode.spec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
spec:
id: spec-vscode
version: "5.1.1"
version: "6.0.0"
status: draft
tier: 2

Expand Down Expand Up @@ -71,7 +71,7 @@ spec:

constraints:
- id: C-01
description: "MUST discover the specter binary in this resolution order: workspace setting specter.binaryPath → PATH → ~/.specter/bin/specter → auto-download from GitHub Releases. MUST verify SHA256 checksum on auto-download. MUST validate the resolved binary (magic-byte check and `--version` probe) regardless of resolution source; a corrupt file on PATH must not slip through as a valid binary. MUST expose a `Specter: Re-download CLI` command and MUST surface CLI invocation failures via an output channel reachable from the status bar (never leave the UI stuck on an indefinite loading state)."
description: "MUST discover the specter binary in this resolution order: workspace setting specter.binaryPath → PATH → ~/.specter/bin/specter → the extension's private copy under ~/.specter/cli, downloaded from GitHub Releases when absent. A PATH or ~/.specter/bin candidate is used only when its version satisfies the range C-34 declares; one outside the range is skipped and never modified. MUST verify SHA256 checksum on auto-download. MUST validate the resolved binary (magic-byte check and `--version` probe) regardless of resolution source; a corrupt file on PATH must not slip through as a valid binary. MUST expose a `Specter: Re-download CLI` command and MUST surface CLI invocation failures via an output channel reachable from the status bar (never leave the UI stuck on an indefinite loading state)."
type: technical
enforcement: error

Expand Down Expand Up @@ -201,7 +201,7 @@ spec:
enforcement: error

- id: C-27
description: "The CLI auto-download MUST default to the CLI version matching the extension's own version — a v0.10.0 extension fetches v0.10.0 CLI, not whatever GitHub's /releases/latest currently returns. The extension reads its own version via ctx.extension.packageJSON.version at download time. Users MAY override via specter.version: set to 'latest' to track GitHub's newest release, or pin a specific semver (e.g. '0.9.2'). Default-to-latest is prohibited because the CLI release and extension Marketplace publish are decoupled — a GoReleaser-produced CLI release fires on tag push, but the matching extension may publish days later (or not at all), creating split-brain installs where users run an older extension against a newer CLI."
description: "The extension's private copy (C-34) MUST default to the CLI version matching the extension's own version — a v0.10.0 extension fetches v0.10.0 CLI, not whatever GitHub's /releases/latest currently returns. The extension reads its own version via ctx.extension.packageJSON.version at download time. Users MAY override via specter.version: set to 'latest' to track GitHub's newest release, or pin a specific semver (e.g. '0.9.2'). Default-to-latest is prohibited because the CLI release and extension Marketplace publish are decoupled — a GoReleaser-produced CLI release fires on tag push, but the matching extension may publish days later (or not at all), creating split-brain installs where users run an older extension against a newer CLI. The private copy is how the extension guarantees itself a compatible CLI. It is not how the user's shell gets one: a newer CLI on PATH is used as is while it satisfies the C-34 range, and is never replaced."
type: business
enforcement: error

Expand Down Expand Up @@ -511,6 +511,11 @@ spec:
type: technical
enforcement: error

- id: C-34
description: "The extension MUST keep the CLI it runs separate from the CLI the user's shell and git hooks run. Its own copy lives at ~/.specter/cli/specter-<version> (with .exe on Windows), one file per version, and the extension MAY download, replace, or delete files there. The extension MUST NOT write, replace, or delete ~/.specter/bin/specter, any binary found on PATH, or the file named by specter.binaryPath: not at activation, not on a version mismatch, and not from the Re-download command. The one permitted write to ~/.specter/bin/specter is the Add CLI to Shell PATH command copying the private binary there when no file exists, as an explicit user action; when a file exists the command MUST leave it alone and say so. The extension MUST declare, in package.json under `specterCli.range`, the CLI version range it supports, in the form `>=A.B.C <X.Y.Z`. A PATH or ~/.specter/bin binary whose `--version` satisfies the range MUST be used as is, with no download. A candidate outside the range MUST be skipped, with a line in the Output channel naming its version and the range, and the private copy used instead. The range MUST include the CLI version the repository ships, and a test binds that. Rationale: an auto-update on version mismatch, present since v0.6.5 and never specified, replaced a newer CLI in ~/.specter/bin with the extension's older one on every activation, silently. The shell PATH command had placed ~/.specter/bin first on PATH, so the pre-push hook ran whatever the extension pinned, and a CLI-only release could not reach hook users at all. The range keeps C-27's concern: deviation is allowed only within the versions the extension's wire types were built against. That holds only while patch releases leave every --json shape the extension reads unchanged, which the type parity tests bind."
type: technical
enforcement: error

acceptance_criteria:
- id: AC-01
description: "Extension activates in a workspace containing specter.yaml; does NOT activate in a workspace with only generic YAML files"
Expand Down Expand Up @@ -1611,6 +1616,54 @@ spec:
references_constraints: ["C-33"]
priority: critical

- id: AC-80
description: "A CLI found on PATH or at ~/.specter/bin/specter is used as is when its version satisfies the declared range, and skipped without being touched when it does not. With the extension at 0.15.0 declaring `>=0.15.0 <0.16.0`: a PATH binary reporting 0.15.1 resolves with source `path`, no download is planned, and no write is planned. A PATH binary reporting 0.16.0 is skipped, the plan names it with its version and the range for the Output channel, the private copy for 0.15.0 is resolved with a download planned if it is absent, and the PATH file is not in any write. A ~/.specter/bin/specter reporting 0.14.1 is skipped the same way. A candidate whose version cannot be read is not a candidate."
inputs:
extension_version: "0.15.0"
declared_range: ">=0.15.0 <0.16.0"
path_candidate_in_range: "0.15.1"
path_candidate_above_range: "0.16.0"
user_dir_candidate_below_range: "0.14.1"
expected_output:
in_range_source: "path"
in_range_download_planned: false
in_range_writes: []
out_of_range_source: "private"
out_of_range_skipped_names_version_and_range: true
out_of_range_writes_touching_candidate: []
references_constraints: ["C-34", "C-01"]
priority: critical

- id: AC-81
description: "Nothing automatic writes ~/.specter/bin/specter. With no candidate anywhere, the plan downloads to ~/.specter/cli/specter-0.15.0 (with .exe on Windows) and to nothing else. The Re-download command's plan targets the same private path and no other. The Add CLI to Shell PATH command copies the private binary to ~/.specter/bin/specter only when no file exists there; when one exists, it leaves the bytes unchanged and reports that it left the file alone."
inputs:
no_candidates: "no PATH binary, no ~/.specter/bin/specter, no private copy"
redownload: "the Re-download command with a private copy present"
shell_path_absent: "the shell PATH command with no ~/.specter/bin/specter"
shell_path_present: "the shell PATH command with an existing ~/.specter/bin/specter of different bytes"
expected_output:
download_target: "~/.specter/cli/specter-0.15.0"
user_dir_in_any_automatic_write: false
shell_path_absent_writes_user_copy: true
shell_path_present_bytes_unchanged: true
shell_path_present_reports_left_alone: true
references_constraints: ["C-34"]
priority: critical

- id: AC-82
description: "package.json declares `specterCli.range` in the form `>=A.B.C <X.Y.Z`, and the repository's VERSION file satisfies it, so the extension that ships accepts the CLI that ships with it. satisfiesRange treats the lower bound as inclusive and the upper bound as exclusive, compares numerically so 0.15.10 is above 0.15.9, ignores a pre-release suffix on the candidate, and rejects any range not in that form."
inputs:
range: ">=0.15.0 <0.16.0"
candidates: "0.15.0, 0.15.1, 0.15.10, 0.16.0, 0.14.9, 0.15.2-rc.1"
malformed: "^0.15.0, >=0.15.0, 0.15.x"
expected_output:
satisfies: "0.15.0, 0.15.1, 0.15.10, 0.15.2-rc.1"
does_not_satisfy: "0.16.0, 0.14.9"
malformed_rejected: true
repository_version_satisfies_declared_range: true
references_constraints: ["C-34"]
priority: high

depends_on:
- spec_id: spec-parse
version_range: "^1.1.0"
Expand All @@ -1629,6 +1682,11 @@ spec:
relationship: requires

changelog:
- version: "6.0.0"
date: "2026-09-14"
author: "specter-team"
type: major
description: "C-34 separates the CLI the extension runs from the CLI the user's shell runs. The extension keeps a private copy per version under ~/.specter/cli and never writes ~/.specter/bin/specter, a PATH binary, or specter.binaryPath; the one exception is the shell PATH command creating the user copy when none exists. It declares a supported CLI range in package.json and uses any PATH or user-dir binary inside it as is, skipping and never modifying one outside it. C-01's order now ends at the private copy, and C-27's default-version rule now governs that copy. AC-80 binds the range gate on both sides and that a skipped candidate is untouched, AC-81 binds that nothing automatic writes the user's copy, and AC-82 binds the range form and that the repository's VERSION satisfies it. Major: the auto-update on version mismatch that a conforming implementation performed since v0.6.5 is now forbidden. It downgraded a 0.15.1 in ~/.specter/bin to 0.15.0 on every activation after the CLI-only v0.15.1 release, with no error, and because the shell PATH command put that directory first on PATH, the pre-push hook ran the downgraded binary."
- version: "5.1.1"
date: "2026-09-01"
author: "specter-team"
Expand Down
12 changes: 6 additions & 6 deletions specter/vscode-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ The annotations are plain comments, no build step, no framework, works in any la

| Setting | Default | Description |
|---|---|---|
| `specter.binaryPath` | `""` | Path to the specter binary. Leave empty to auto-resolve. Machine-scoped; workspace settings are ignored. |
| `specter.autoDownload` | `true` | Download specter automatically if not found. |
| `specter.version` | `""` | Binary version to download. Empty means "match the extension version"; `latest` tracks the newest GitHub release. Machine-scoped; workspace settings are ignored. |
| `specter.binaryPath` | `""` | Path to the specter binary. Leave empty to auto-resolve: a CLI on PATH or at `~/.specter/bin/specter` is used when its version is one the extension supports, and otherwise the extension uses its own copy under `~/.specter/cli`. A path set here must be a working binary that answers `--version`, but it is not checked against the supported range, because it is your explicit choice. A path that does not exist is ignored and resolution continues. Machine-scoped; workspace settings are ignored. |
| `specter.autoDownload` | `true` | Download the extension's own CLI copy when no supported CLI is found. |
| `specter.version` | `""` | Version of the extension's own CLI copy. Empty means "match the extension version"; `latest` tracks the newest GitHub release. A supported CLI on PATH is used regardless. Machine-scoped; workspace settings are ignored. |
| `specter.showInsightsOnFailure` | `true` | Open Insights panel automatically when a spec fails threshold. |

---
Expand All @@ -146,16 +146,16 @@ The annotations are plain comments, no build step, no framework, works in any la
| `Specter: Copy Spec Context for AI` | Copy current spec as a structured AI prompt preamble |
| `Specter: Run Sync` | Re-run the full coverage pipeline manually |
| `Specter: Run Reverse Compiler` | Generate draft specs from your source code |
| `Specter: Add CLI to Shell PATH` | Append `~/.specter/bin` to your shell rc file so `specter` works in external terminals |
| `Specter: Re-download CLI` | Force a fresh download of the CLI binary (recovery if the cached one is broken) |
| `Specter: Add CLI to Shell PATH` | Install a copy of the CLI at `~/.specter/bin/specter` if none is there, and append `~/.specter/bin` to your shell rc file so `specter` works in external terminals. An existing file there is left alone. When you already have a working CLI on PATH, in the supported range or not, the command does nothing, so it never puts a copy ahead of yours. |
| `Specter: Re-download CLI` | Force a fresh download of the extension's own CLI copy (recovery if it is broken). Your `~/.specter/bin/specter` is not touched. |
| `Specter: Show Output Log` | Open the Specter output channel with download/coverage error details |
| `Specter: Reveal in Tree View` | Jump to the current spec in the Coverage sidebar |

---

## Using `specter` from external terminals

When the extension auto-downloads the CLI, it lands at `~/.specter/bin/specter`. VS Code's integrated terminal gets this path prepended automatically. External terminals (iTerm, Windows Terminal, tmux, etc.) don't, you'd need to type the full path.
The extension keeps its own CLI copy under `~/.specter/cli`, one file per version, and never changes a CLI you installed yourself. `~/.specter/bin/specter` is yours: the extension creates it only when you run the shell PATH command and nothing is there, and it never replaces it afterward. VS Code's integrated terminal gets `~/.specter/bin` prepended automatically. External terminals (iTerm, Windows Terminal, tmux, etc.) don't, you'd need to type the full path. If you have no CLI of your own on PATH, typing `specter` there yourself needs that copy to exist, so run the shell PATH command once. The extension's own commands that open a terminal, such as Run Reverse Compiler and View Diff, use the CLI the extension resolved and need no setup.

Run `Specter: Add CLI to Shell PATH` from the command palette once, and the extension will append an idempotent export to your shell's rc file (`.bashrc` on Linux, `.bash_profile` on macOS, `.zshrc` for zsh, `config.fish` for fish). Restart your terminal and `specter` works from anywhere.

Expand Down
Loading
Loading