Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,4 @@ CLAUDE.md

# Pnpm
.pnpm-store/
result
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ These tools automatically read workflow instructions from `openspec/AGENTS.md`.

#### Step 1: Install the CLI globally

**Option A: Using npm**

```bash
npm install -g @fission-ai/openspec@latest
```
Expand All @@ -144,6 +146,39 @@ Verify installation:
openspec --version
```

**Option B: Using Nix (NixOS and Nix package manager)**

Run OpenSpec directly without installation:
```bash
nix run github:Fission-AI/OpenSpec -- init
```

Or install to your profile:
```bash
nix profile install github:Fission-AI/OpenSpec
```

Or add to your development environment in `flake.nix`:
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
openspec.url = "github:Fission-AI/OpenSpec";
};

outputs = { nixpkgs, openspec, ... }: {
devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
buildInputs = [ openspec.packages.x86_64-linux.default ];
};
};
}
```

Verify installation:
```bash
openspec --version
```

#### Step 2: Initialize OpenSpec in your project

Navigate to your project directory:
Expand Down
27 changes: 27 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 87 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
description = "OpenSpec - AI-native system for spec-driven development";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};

outputs = { self, nixpkgs }:
let
supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];

forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
in
{
packages = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
default = pkgs.stdenv.mkDerivation (finalAttrs: {
pname = "openspec";
version = "0.18.0";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see there's a hardcoded version here? Do we have a way to make this dynamic?

In general what I'm trying to figure out is how does this fit into the standard release process, do we need to add any CI/CD for this that works alongside the standard release process?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think package.json could be imported in the flake. Will have a look for this tomorrow (dutch time). I can also create a nix build script in the github actions to prove it's working correctly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Add scripts/update-flake.sh to automate flake version/hash updates
  • Add scripts/README.md documenting maintenance scripts

Maintainers can update flake.nix with:
./scripts/update-flake.sh

Implemented following OpenSpec spec-driven workflow:
openspec/changes/archive/2026-01-09-add-flake-update-script/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll create a github-action next week. Have a nice weekend down under.


src = ./.;

pnpmDeps = pkgs.fetchPnpmDeps {
inherit (finalAttrs) pname version src;
pnpm = pkgs.pnpm_9;
fetcherVersion = 3;
hash = "sha256-ltG+wmr2aiRmghEMGOZVYifvrY8qdLDfizLq6a8Ds0s=";
};

nativeBuildInputs = with pkgs; [
nodejs_20
npmHooks.npmInstallHook
pnpmConfigHook
pnpm_9
];

buildPhase = ''
runHook preBuild

pnpm run build

runHook postBuild
'';

dontNpmPrune = true;

meta = with pkgs.lib; {
description = "AI-native system for spec-driven development";
homepage = "https://github.com/Fission-AI/OpenSpec";
license = licenses.mit;
maintainers = [ ];
mainProgram = "openspec";
};
});
});

apps = forAllSystems (system: {
default = {
type = "app";
program = "${self.packages.${system}.default}/bin/openspec";
};
});

devShells = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs_20
pnpm_9
];

shellHook = ''
echo "OpenSpec development environment"
echo "Node version: $(node --version)"
echo "pnpm version: $(pnpm --version)"
echo "Run 'pnpm install' to install dependencies"
'';
};
});
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-07
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
## Context

OpenSpec is a TypeScript CLI tool using pnpm for dependency management. The project requires Node.js ≥20.19.0. Nix uses its own build system that needs to understand how to fetch dependencies and build the project reproducibly.

The Nix ecosystem has specific patterns for packaging Node.js/pnpm projects that differ from the traditional npm ecosystem.

## Goals

- Enable OpenSpec to be run directly via `nix run github:Fission-AI/OpenSpec`
- Support all major platforms (Linux x86/ARM, macOS x86/ARM)
- Use existing pnpm-lock.yaml for reproducible builds
- Provide development environment for Nix users

## Non-Goals

- Replace existing npm/pnpm publishing workflow
- Publish to nixpkgs (can be done later as separate effort)
- Support Windows (Nix doesn't run natively on Windows)

## Decisions

### Use stdenv.mkDerivation instead of buildNpmPackage

**Decision**: Package OpenSpec using `stdenv.mkDerivation` with pnpm hooks.

**Rationale**: The zigbee2mqtt package in nixpkgs demonstrates the current best practice for pnpm projects. Using `buildNpmPackage` with pnpm requires complex configuration, while `mkDerivation` with the right hooks is more straightforward and better supported.

**Alternative considered**: Using `buildNpmPackage` with `npmConfigHook = pkgs.pnpmConfigHook` - this is the older pattern and causes issues with dependency fetching.

### Use fetchPnpmDeps with explicit pnpm version

**Decision**: Use `pkgs.fetchPnpmDeps` with `pnpm = pkgs.pnpm_9` and `fetcherVersion = 3`.

**Rationale**:
- pnpm lockfile version 9.0 requires fetcherVersion 3
- Explicit pnpm_9 ensures consistency between fetch and build
- This is the documented way to handle pnpm projects in nixpkgs

### Multi-platform support without flake-utils

**Decision**: Implement multi-platform support using plain Nix with `nixpkgs.lib.genAttrs`.

**Rationale**: Per user request, avoid extra dependencies. The `genAttrs` pattern is simple and well-understood in the Nix community.

### Node.js 20 instead of latest

**Decision**: Pin to nodejs_20 to match package.json engines requirement.

**Rationale**: Ensures consistency with development environment and npm package requirements. Avoids potential compatibility issues with newer Node versions.

## Key Implementation Details

### Dependency Hash Management

The `pnpmDeps.hash` field must be updated whenever dependencies change. The workflow:
1. Set hash to fake value (all zeros)
2. Run `nix build`
3. Nix fails with actual hash
4. Update flake.nix with correct hash

This is standard Nix workflow for fixed-output derivations.

### Build Inputs

Required nativeBuildInputs:
- `nodejs_20` - runtime
- `npmHooks.npmInstallHook` - handles installation phase
- `pnpmConfigHook` - configures pnpm environment
- `pnpm_9` - pnpm executable

The `dontNpmPrune = true` is important to keep all dependencies after build.

## Risks / Trade-offs

**[Risk]** Hash needs updating when dependencies change → **Mitigation**: Document this clearly; error message from Nix provides correct hash

**[Risk]** Nix builds might lag behind npm releases → **Mitigation**: This is fine; Nix users can still use npm if they need bleeding edge

**[Trade-off]** Additional maintenance burden for hash updates → **Benefit**: Better experience for Nix ecosystem users

## Migration Plan

1. Add flake.nix to repository
2. Test builds on multiple platforms (can use GitHub Actions with Nix)
3. Update README with Nix installation instructions
4. Optionally add to CI pipeline to catch hash mismatches early

No breaking changes - this is purely additive.

## Open Questions

- Should we add automatic hash updating to CI? (Could use nix-update-script)
- Should we submit to nixpkgs after validation? (Separate decision)
- Do we want to support older Node versions in flake? (Probably no - stick to package.json requirement)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Why

OpenSpec users on NixOS or using the Nix package manager cannot easily install or run OpenSpec without going through npm. Adding a Nix flake makes OpenSpec a first-class citizen in the Nix ecosystem, enabling users to run `nix run github:Fission-AI/OpenSpec -- init` or include OpenSpec in their development environments declaratively.

## What Changes

- Add `flake.nix` to repository root with multi-platform support (x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin)
- Package uses pnpm for dependency management (matching existing development workflow)
- Support both direct execution via `nix run` and installation via `nix profile install`
- Provide dev shell for contributors using Nix

## Capabilities

### New Capabilities
- `nix-flake-support`: Nix flake configuration for building and running OpenSpec

### Modified Capabilities
- None

## Impact

- **New files**: `flake.nix` in repository root
- **Documentation**: Should add installation instructions for Nix users
- **CI/CD**: Could add flake checking to CI pipeline (optional)
- **Maintenance**: Requires updating pnpmDeps hash when dependencies change
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## ADDED Requirements

### Requirement: Multi-platform Nix flake
The system SHALL provide a Nix flake that builds OpenSpec for multiple platforms.

#### Scenario: Build on Linux x86_64
- **WHEN** user runs `nix build` on x86_64-linux system
- **THEN** system builds OpenSpec package successfully
- **AND** package includes the `openspec` binary

#### Scenario: Build on macOS ARM
- **WHEN** user runs `nix build` on aarch64-darwin system
- **THEN** system builds OpenSpec package successfully
- **AND** package includes the `openspec` binary

#### Scenario: Build on Linux ARM
- **WHEN** user runs `nix build` on aarch64-linux system
- **THEN** system builds OpenSpec package successfully

#### Scenario: Build on macOS x86_64
- **WHEN** user runs `nix build` on x86_64-darwin system
- **THEN** system builds OpenSpec package successfully

### Requirement: Direct execution via nix run
The system SHALL allow users to run OpenSpec directly from GitHub without installing.

#### Scenario: Run init command from GitHub
- **WHEN** user runs `nix run github:Fission-AI/OpenSpec -- init`
- **THEN** system downloads and builds OpenSpec
- **AND** executes `openspec init` command

#### Scenario: Run any OpenSpec command
- **WHEN** user runs `nix run github:Fission-AI/OpenSpec -- <command> <args>`
- **THEN** system executes `openspec <command> <args>`

### Requirement: pnpm dependency management
The system SHALL use pnpm for building OpenSpec in the Nix flake.

#### Scenario: Fetch dependencies with pnpm
- **WHEN** Nix builds the package
- **THEN** system uses `fetchPnpmDeps` to download dependencies
- **AND** uses pnpm-lock.yaml for reproducible builds
- **AND** uses fetcherVersion 3 for lockfile version 9.0

#### Scenario: Build with pnpm
- **WHEN** Nix runs the build phase
- **THEN** system executes `pnpm run build`
- **AND** produces dist directory with compiled TypeScript

### Requirement: Node.js version compatibility
The system SHALL use Node.js 20 as specified in package.json engines field.

#### Scenario: Build with correct Node version
- **WHEN** Nix builds OpenSpec
- **THEN** system uses nodejs_20 from nixpkgs
- **AND** build succeeds without version compatibility errors

### Requirement: Development shell
The system SHALL provide a Nix development shell for contributors.

#### Scenario: Enter dev shell
- **WHEN** user runs `nix develop` in OpenSpec repository
- **THEN** system provides shell with nodejs_20 and pnpm_9
- **AND** displays welcome message with versions
- **AND** provides instructions to run `pnpm install`

### Requirement: Proper binary installation
The system SHALL install the openspec binary correctly.

#### Scenario: Binary in PATH
- **WHEN** package is built or installed
- **THEN** `openspec` binary is available in `$out/bin/openspec`
- **AND** binary is executable
- **AND** binary can be invoked without full path when installed

#### Scenario: Binary executes correctly
- **WHEN** user runs the installed `openspec` command
- **THEN** system executes the CLI entry point
- **AND** all subcommands work correctly
Loading