* fix(packaging): print the completions tip from the CLI, not a postinstall script The package's only install script existed to print one line suggesting `openspec completion install`. Shipping it made every `npm install -g` emit an npm allow-scripts warning, and `npm approve-scripts` then failed with ENOMATCH because it looks in the local project, not a global install — so the warning looked like a packaging fault with no way to clear it. The tip now prints once on the CLI's first run, recorded via a `completionTipSeen` flag in the existing global config alongside the telemetry notice's `noticeSeen`. It writes to stderr so it can never contaminate piped stdout, and is suppressed under CI, OPENSPEC_NO_COMPLETIONS=1, `--json` runs, and `openspec completion` itself. The published package now ships no lifecycle scripts at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(completions): stop the first-run tip from corrupting global config Adversarial review of the previous commit found it wrote a defaults-merged config: `saveGlobalConfig({ ...getGlobalConfig(), completionTipSeen: true })` stamped `profile: "core"` into every user's config.json on first run. `migrateIfNeeded` treats a raw `profile` as "already migrated", so the one-time profile migration would never run again — and `openspec update` then deleted the user's installed workflow skills. Reproduced: 2 skill directories removed where main reports "Migrated: custom profile with 8 workflows". The same write also overwrote an unparsable config with defaults and made `openspec config list` report defaults as explicit. The tip now reads and writes the raw config file and touches only its own key, leaving an unreadable config strictly alone. Other hardening from the same review: - Suppress the tip for the hidden `__complete` resolver. Generated completion scripts call it on every Tab press with stderr discarded, so the one-shot tip was consumed where nobody could see it. - Defer, never consume, when stderr is not a terminal. Agents and pipes drive this CLI far more often than humans do and would otherwise spend the tip into a log nobody opens. - Skip the tip when completions are already installed. Previously the CLI advertised `completion install` to users who had run it — including on the very next command after installing. Adds `isInstalled()` to the bash/fish/powershell installers, mirroring the zsh one. - Use the repo's `isCiEnvironment()` instead of a `CI === 'true'` string check, so `CI=yes`/`True`/`on` are as quiet as telemetry is. - Move the call to `postAction` so the tip trails the command's output instead of pushing errors and `init`'s setup summary down the screen. - Record before printing, so an unwritable config dir means silence rather than nagging on every run. Tests: assert the message literal (mutation testing showed the message text was the one unguarded behavior), the raw-write shape, corrupt-config safety, the already-installed path, the defer policy, and an e2e case pinning the non-TTY contract. Docs: SECURITY.md no longer claims zero lifecycle scripts — `prepare` is still declared and runs for git/directory installs; the registry-install claim is the accurate one. `OPENSPEC_NO_COMPLETIONS` is now documented. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(completions): make the unwritable-config case portable to Windows fs.chmodSync(dir, 0o555) does not stop a write on Windows, so this test's unwritable condition never existed there: markTipSeen succeeded, the tip printed, and windows-pwsh was the only failing job. Occupy the config directory's path with a file instead. mkdirSync with recursive: true tolerates an existing directory but throws on an existing file on every platform, so the persist fails where a real permission error would - before anything is printed. Also asserts the path is still a file, so a partial write through the failure would be caught. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(completions): retire the first-run tip instead of advising a dead end Second adversarial pass over the tip, covering the hardening commit itself. - An undetected or unsupported shell now retires the tip quietly. It used to print, but `openspec completion install` exits 1 for exactly those users ("Shell 'tcsh' is not supported yet" / "Could not auto-detect shell"), so the one message they would ever get about completions sent them to a command that fails. - `markTipSeen` re-reads the config immediately before writing and swaps the file in by rename. Deciding whether to show the tip costs a `ps` spawn plus a stat, and a sibling process writing config in that window got clobbered — on a first run that is exactly when telemetry mints `anonymousId`. Concurrent-process loss drops from 15/40 to ~2/40, and what now usually loses is the tip's own flag (it simply shows once more) rather than telemetry identity. The residual is the non-atomic read-modify-write shape shared with telemetry's own writer. - `isInstalled()` uses stat().isFile(), so a directory at the install path no longer counts as an installed completion script. - Documented what `isInstalled()` actually promises: the script file, not the profile sourcing line that bash and PowerShell also need. Callers deciding whether to *advertise* completions want the loose reading — a user whose profile config failed has already met the installer. - Corrected a comment claiming the probe costs "one stat": detectShell() forks `ps` to read the parent process on every non-Windows run. Tests: mutation testing found four surviving mutants — dropping isCompletionRun from the defer policy, reverting isCiEnvironment to a CI==='true' string check, failing closed on an undetected shell, and neutering the non-object config guard (which lets a JSON array config be rewritten as {"0":...}). All four now fail a test. Adds direct coverage for the three new isInstalled() implementations, which had none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(validate): stop `change validate` exiting past commander's postAction `change validate` on a failing change called process.exit(exitCode). That tears down before commander's postAction hook, which is the same trap the `update` command documents 165 lines earlier: "exiting here would skip commander's postAction hook, killing the telemetry flush mid-request". A change that fails validation is a routine outcome, not an error, so this silently dropped the telemetry flush and — since the completions tip moved to postAction — the first-run tip for anyone whose first command was a failing validate. Verified under a pty: before, the tip never printed and completionTipSeen was never recorded; after, both happen and the exit code is still 1 (validate() already sets process.exitCode, which Node honours at natural exit — top-level `validate --all` has always relied on exactly that). The existing e2e in validate-scenario-loss.test.ts pins the exit code. Also wraps the postAction tip in try/finally so the telemetry flush runs even if the hint throws: program.parse() is synchronous, so a rejection there has no catch above it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
OpenSpec Documentation
Welcome. This is the home for everything OpenSpec.
OpenSpec helps you and your AI coding assistant agree on what to build before any code is written. You describe the change, the AI drafts a short spec and a task list, you both look at the same plan, and then the work happens. No more discovering halfway through that the AI built the wrong thing.
If you read nothing else, read these two pages:
- Getting Started: install, initialize, and ship your first change.
- How Commands Work: where you actually type
/opsx:propose(hint: in your AI chat, not the terminal). This trips up almost everyone once.
That second one matters more than it looks. OpenSpec has two halves: a command line tool you run in your terminal, and slash commands you give to your AI assistant. Knowing which is which saves you the most common moment of confusion.
The best habit to build first: when you're not sure what to build, start with
/opsx:explore. It's a no-stakes thinking partner that reads your code, weighs options, and sharpens a fuzzy idea into a concrete plan before any artifact or code exists. The Explore First guide makes the case.
Pick your path
I'm brand new. Start with Getting Started, then skim the Core Concepts at a Glance. When something feels mysterious, the FAQ and Glossary are nearby.
I have a problem but not a plan. This is the common case, and it has a dedicated answer: Explore First. Use /opsx:explore to think it through with the AI before committing to anything.
I have a big existing codebase. You don't document all of it. Using OpenSpec in an Existing Project shows how to start on real, brownfield code without boiling the ocean.
I just want to get it working. Install, run openspec init, then read How Commands Work so your first slash command lands in the right place. Or hand the setup to your assistant with the AI-assisted install prompt.
I learn by example. The Examples & Recipes page walks through real changes start to finish: a small feature, a bug fix, a refactor, an exploration.
The AI just drafted a plan — now what? Read it. Reviewing a Change shows the two-minute pass that catches a wrong turn while it's still cheap, and Writing Good Specs covers what a plan worth approving is made of.
I work on a team. OpenSpec on a Team shows how a change maps onto a branch and a pull request, and how teammates review a plan before the code.
I'm coming from the old workflow. The Migration Guide explains what changed and why, and promises your existing work is safe.
I want to bend it to my team's process. Customization covers project config, custom schemas, and shared context.
Something's broken. Troubleshooting collects the failures people actually hit, with fixes.
The whole map
Start here
| Doc | What it gives you |
|---|---|
| Getting Started | Install, initialize, and run your first change end to end |
| Explore First | Use /opsx:explore to think through an idea before you commit |
| How Commands Work | Where slash commands run, what "interactive mode" means, terminal vs chat |
| Core Concepts at a Glance | The whole mental model on one page: specs, changes, deltas, archive |
| Installation | npm, pnpm, yarn, bun, Nix, a prompt that hands setup to your AI assistant, and how to verify it worked |
Use it day to day
| Doc | What it gives you |
|---|---|
| Workflows | Common patterns and when to reach for each command |
| Examples & Recipes | Full walkthroughs of real changes, copy-pasteable |
| Writing Good Specs | What a strong requirement and scenario look like, and how to right-size a change |
| Reviewing a Change | The two-minute pass on a drafted plan before any code is written |
| OpenSpec on a Team | How changes fit branches, pull requests, and review |
| Using OpenSpec in an Existing Project | Adopting OpenSpec on a large brownfield codebase |
| Editing & Iterating on a Change | Update artifacts, go back, reconcile manual edits |
| Commands | Reference for every /opsx:* slash command |
| CLI | Reference for every openspec terminal command |
Understand it deeply
| Doc | What it gives you |
|---|---|
| Concepts | The long-form explanation of specs, changes, artifacts, schemas, and archive |
| OPSX Workflow | Why the workflow is fluid instead of phase-locked, plus an architecture deep dive |
| Glossary | Every term defined in one place |
Make it yours
| Doc | What it gives you |
|---|---|
| Customization | Project config, custom schemas, shared context |
| Multi-Language | Generate artifacts in languages other than English |
| Supported Tools | The 30+ AI tools OpenSpec integrates with, and where files land |
When you need help
| Doc | What it gives you |
|---|---|
| FAQ | Quick answers to the questions people ask most |
| Troubleshooting | Concrete fixes for concrete failures |
| Migration Guide | Moving from the legacy workflow to OPSX |
Coordinate across repos (beta)
| Doc | What it gives you |
|---|---|
| Stores: User Guide | Plan in its own repo when your work spans repos or teams |
| Agent Contract | The machine-readable CLI surfaces agents drive |
The thirty-second version
1. Install npm install -g @fission-ai/openspec@latest
2. Initialize cd your-project && openspec init
3. Explore (in your AI chat) /opsx:explore ← optional, but a great habit
4. Propose (in your AI chat) /opsx:propose add-dark-mode
5. Build (in your AI chat) /opsx:apply
6. Archive (in your AI chat) /opsx:archive
Steps 1 and 2 happen in your terminal. The rest happen in your AI assistant's chat. That split is the one thing worth memorizing, and How Commands Work explains exactly why. Step 3 is optional, but starting with /opsx:explore when you're unsure is the habit most worth forming.
Where else to get help
- Discord: discord.gg/YctCnvvshC for questions, ideas, and help.
- GitHub Issues: github.com/Fission-AI/OpenSpec/issues for bugs and feature requests.
openspec feedback "your message"sends feedback straight from your terminal (it opens a GitHub issue).
Found something in these docs that's wrong, stale, or confusing? That's a bug. Open an issue or a PR. Documentation improvements are some of the most valuable contributions you can make.