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
2 changes: 2 additions & 0 deletions openspec/changes/schema-management-cli/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-20
113 changes: 113 additions & 0 deletions openspec/changes/schema-management-cli/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
## Context

OpenSpec uses workflow schemas to define artifact sequences for change proposals. Currently, schemas are resolved from three locations (project → user → package), but managing custom schemas requires manual file creation with no tooling support. The resolver infrastructure exists (`src/core/artifact-graph/resolver.ts`) but there's no CLI exposure for schema management operations.

Users who want to customize workflows must:
1. Manually create directory structures under `openspec/schemas/<name>/`
2. Copy and modify `schema.yaml` files without validation
3. Debug resolution issues by inspecting the filesystem directly

This creates friction for schema customization and leads to runtime errors when schemas are malformed.

## Goals / Non-Goals

**Goals:**
- Provide CLI commands for common schema management operations
- Enable interactive schema creation with guided prompts
- Allow forking existing schemas as customization starting points
- Surface schema validation errors before runtime
- Help debug schema resolution order when shadowing occurs

**Non-Goals:**
- Schema editing (users edit YAML directly or via `$EDITOR`)
- Schema publishing or sharing mechanisms
- Schema versioning or migration tooling
- Validation of template file contents (only checks existence)
- Schema inheritance or composition beyond simple forking

## Decisions

### 1. Command Structure: `openspec schema <subcommand>`

Add a new command group following the existing pattern used by `openspec config` and `openspec completion`.

**Rationale:** Grouping related commands under a noun (schema) matches the established CLI patterns and provides a natural namespace for future schema operations.

**Alternatives considered:**
- Flat commands (`openspec schema-init`, `openspec schema-fork`): Rejected because it pollutes the top-level namespace and doesn't scale well.
- Extending existing commands (`openspec init --schema`): Rejected because schema management is distinct from project initialization.

### 2. Implementation Location

New file `src/commands/schema.ts` with a `registerSchemaCommand(program: Command)` function that registers the `schema` command group and all subcommands.

**Rationale:** Follows the pattern established by `config.ts` and matches how other command groups are organized.

### 3. Schema Validation Approach

Validation checks:
1. `schema.yaml` exists and is valid YAML
2. Parses successfully against the Zod schema in `types.ts`
3. All referenced template files exist in the schema directory
4. Artifact dependency graph has no cycles (use existing topological sort)

**Rationale:** Reuse existing validation infrastructure (`parseSchema` from `schema.ts`) and extend with template existence checks. This catches the most common errors without duplicating validation logic.

**Alternatives considered:**
- Deep template validation (check frontmatter, syntax): Rejected as over-engineering. Template contents are free-form markdown.

### 4. Interactive Prompts for `schema init`

Use `@inquirer/prompts` (already a dependency) for:
- Schema name input with kebab-case validation
- Schema description input
- Multi-select for artifact selection with descriptions
- Optional: set as project default

**Rationale:** Matches the UX established by `openspec init` and `openspec config reset`. Provides a guided experience while keeping the wizard lightweight.

### 5. Fork Source Resolution

`schema fork <source>` resolves the source schema using the existing `getSchemaDir()` function, respecting the full resolution order (project → user → package). This allows forking from any accessible schema.

The destination is always project-local: `openspec/schemas/<name>/`

**Rationale:** Forking to project scope makes sense because:
- Custom schemas are project-specific decisions
- User-global schemas can be added manually if needed
- Keeps the command simple with a clear default

### 6. Output Format Consistency

All commands support `--json` flag for machine-readable output:
- `schema init`: Outputs `{ "created": true, "path": "...", "schema": "..." }`
- `schema fork`: Outputs `{ "forked": true, "source": "...", "destination": "..." }`
- `schema validate`: Outputs validation report matching existing validate command format
- `schema which`: Outputs `{ "name": "...", "source": "project|user|package", "path": "..." }`

Text output uses ora spinners for progress and clear success/error messaging.

**Rationale:** Consistent with existing OpenSpec commands and enables scripting/automation.

### 7. Schema `which` Command Design

Shows resolution details for a schema name:
- Which location it resolves from (project/user/package)
- Full path to the schema directory
- Whether it shadows other schemas at lower priority levels

**Rationale:** Essential for debugging "why isn't my schema being used?" scenarios when multiple schemas with the same name exist.

## Risks / Trade-offs

**[Template scaffolding may become stale]** → The `schema init` command will scaffold a default set of artifacts (proposal, specs, design, tasks). If the built-in schema patterns evolve, these templates may not reflect best practices.
- *Mitigation*: Document that `init` creates a minimal starting point. Users can `fork` built-in schemas for the latest patterns.

**[Interactive prompts in CI environments]** → `schema init` with prompts may hang in non-interactive environments.
- *Mitigation*: Support `--name`, `--description`, and `--artifacts` flags for non-interactive use. Detect TTY and show helpful error if prompts would hang.

**[Validation doesn't catch all errors]** → Schema validation checks structure but can't verify semantic correctness (e.g., a template that doesn't match its artifact purpose).
- *Mitigation*: This is acceptable. Full semantic validation would require understanding template intent, which is out of scope.

**[Fork overwrites without warning]** → If target schema already exists, `fork` could overwrite it.
- *Mitigation*: Check for existing schema and require `--force` flag or interactive confirmation before overwriting.
55 changes: 55 additions & 0 deletions openspec/changes/schema-management-cli/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
## Why

Creating and managing project-local schemas currently requires manual directory creation, copying files, and hoping the structure is correct. Users only discover structural errors at runtime when commands fail. This friction discourages schema customization and makes it harder to tailor OpenSpec workflows to specific project needs.

Key pain points:
- **Manual scaffolding**: Users must manually create `openspec/schemas/<name>/` with correct structure
- **No validation feedback**: Schema errors aren't caught until a command tries to use the schema
- **Starting from scratch is hard**: No easy way to base a custom schema on an existing one
- **Debugging resolution**: When a schema doesn't resolve as expected, there's no way to see the resolution path

## What Changes

Add a new `openspec schema` command group with subcommands for creating, forking, validating, and inspecting schemas.

### Commands

1. **`openspec schema init <name>`** - Interactive wizard to scaffold a new project schema
- Prompts for schema description
- Prompts for artifacts to include (with explanations)
- Creates valid directory structure with `schema.yaml` and template files
- Optionally sets as project default in `openspec/config.yaml`

2. **`openspec schema fork <source> [name]`** - Copy an existing schema as a starting point
- Copies from user override or package built-in
- Allows renaming (defaults to `<source>-custom`)
- Preserves all templates and configuration

3. **`openspec schema validate [name]`** - Validate schema structure and templates
- Checks `schema.yaml` is valid
- Verifies all referenced templates exist
- Reports missing or malformed files
- Run without name to validate all project schemas

4. **`openspec schema which <name>`** - Show schema resolution path
- Displays which location the schema resolves from (project/user/package)
- Shows full path to schema directory
- Useful for debugging shadowing issues

## Capabilities

### New Capabilities
- `schema-init-command`: Interactive wizard for creating new project schemas with guided prompts
- `schema-fork-command`: Copy existing schemas to project for customization
- `schema-validate-command`: Validate schema structure and report errors before runtime
- `schema-which-command`: Debug schema resolution by showing which location is used
Comment on lines +40 to +45
Copy link

Choose a reason for hiding this comment

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

logic: Missing required specs/ directory with delta files. Since new capabilities are being added (schema-init-command, schema-fork-command, schema-validate-command, schema-which-command), you must create spec deltas under openspec/changes/schema-management-cli/specs/<capability-name>/spec.md with ## ADDED Requirements sections per openspec/AGENTS.md:177-195.

Each capability needs:

  • At least one requirement with ### Requirement: header
  • At least one scenario per requirement with #### Scenario: header (4 hashtags)
  • WHEN/THEN format for scenarios

See openspec/changes/add-feedback-command/specs/cli-feedback/spec.md for reference.

Prompt To Fix With AI
This is a comment left during a code review.
Path: openspec/changes/schema-management-cli/proposal.md
Line: 40:45

Comment:
**logic:** Missing required `specs/` directory with delta files. Since new capabilities are being added (`schema-init-command`, `schema-fork-command`, `schema-validate-command`, `schema-which-command`), you must create spec deltas under `openspec/changes/schema-management-cli/specs/<capability-name>/spec.md` with `## ADDED Requirements` sections per `openspec/AGENTS.md:177-195`.

Each capability needs:
- At least one requirement with `### Requirement:` header
- At least one scenario per requirement with `#### Scenario:` header (4 hashtags)
- WHEN/THEN format for scenarios

See `openspec/changes/add-feedback-command/specs/cli-feedback/spec.md` for reference.

How can I resolve this? If you propose a fix, please make it concise.


### Modified Capabilities
<!-- None - these are additive commands -->

## Impact

- **Code**: New command implementations in `src/commands/` using existing resolver infrastructure
- **CLI**: New `schema` command group with 4 subcommands
- **Dependencies**: May use `enquirer` or similar for interactive prompts in `schema init`
- **Documentation**: Need to update CLI reference and schema customization guide
Comment on lines +11 to +55
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add required change artifacts (tasks.md and any required delta specs).
This change folder only shows proposal.md and .openspec.yaml, but the process expects openspec/changes/<id>/tasks.md (and delta specs; design.md optional). Please add the missing artifacts. As per learnings, scaffold proposal.md, tasks.md, optional design.md, and delta specs under openspec/changes/<id>/.

🤖 Prompt for AI Agents
In `@openspec/changes/schema-management-cli/proposal.md` around lines 11 - 55, The
change set is missing required artifacts—add a tasks.md and the required delta
spec files under the same change folder as proposal.md (i.e.,
openspec/changes/<id>/tasks.md and openspec/changes/<id>/delta-*.yaml), and
optionally include design.md; ensure .openspec.yaml remains and that tasks.md
lists actionable tasks for implementing the new openspec schema commands (schema
init, fork, validate, which) and references the proposal.md and any delta specs
so the change folder is complete for the release process.

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
## ADDED Requirements

### Requirement: Schema fork copies existing schema
The CLI SHALL provide an `openspec schema fork <source> [name]` command that copies an existing schema to the project's `openspec/schemas/` directory.

#### Scenario: Fork with explicit name
- **WHEN** user runs `openspec schema fork spec-driven my-custom`
- **THEN** system locates `spec-driven` schema using resolution order (project → user → package)
- **AND** copies all files to `openspec/schemas/my-custom/`
- **AND** updates `name` field in `schema.yaml` to `my-custom`
- **AND** displays success message with source and destination paths

#### Scenario: Fork with default name
- **WHEN** user runs `openspec schema fork spec-driven` without specifying a name
- **THEN** system copies to `openspec/schemas/spec-driven-custom/`
- **AND** updates `name` field in `schema.yaml` to `spec-driven-custom`

#### Scenario: Source schema not found
- **WHEN** user runs `openspec schema fork nonexistent`
- **THEN** system displays error that schema was not found
- **AND** lists available schemas
- **AND** exits with non-zero code

### Requirement: Schema fork prevents accidental overwrites
The CLI SHALL require confirmation or `--force` flag when the destination schema already exists.

#### Scenario: Destination exists without force
- **WHEN** user runs `openspec schema fork spec-driven my-custom` and `openspec/schemas/my-custom/` exists
- **THEN** system displays error that destination already exists
- **AND** suggests using `--force` to overwrite
- **AND** exits with non-zero code

#### Scenario: Destination exists with force flag
- **WHEN** user runs `openspec schema fork spec-driven my-custom --force` and destination exists
- **THEN** system removes existing destination directory
- **AND** copies source schema to destination
- **AND** displays success message

#### Scenario: Interactive confirmation for overwrite
- **WHEN** user runs `openspec schema fork spec-driven my-custom` in interactive mode and destination exists
- **THEN** system prompts for confirmation to overwrite
- **AND** proceeds based on user response

### Requirement: Schema fork preserves all schema files
The CLI SHALL copy the complete schema directory including templates, configuration, and any additional files.

#### Scenario: Copy includes template files
- **WHEN** user forks a schema with template files (e.g., `proposal.md`, `design.md`)
- **THEN** all template files are copied to the destination
- **AND** template file contents are unchanged

#### Scenario: Copy includes nested directories
- **WHEN** user forks a schema with nested directories (e.g., `templates/specs/`)
- **THEN** nested directory structure is preserved
- **AND** all nested files are copied

### Requirement: Schema fork outputs JSON format
The CLI SHALL support `--json` flag for machine-readable output.

#### Scenario: JSON output on success
- **WHEN** user runs `openspec schema fork spec-driven my-custom --json`
- **THEN** system outputs JSON with `forked: true`, `source`, `destination`, and `sourcePath` fields

#### Scenario: JSON output shows source location
- **WHEN** user runs `openspec schema fork spec-driven --json`
- **THEN** JSON output includes `sourceLocation` field indicating "project", "user", or "package"
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
## ADDED Requirements

### Requirement: Schema init command creates project-local schema
The CLI SHALL provide an `openspec schema init <name>` command that creates a new schema directory under `openspec/schemas/<name>/` with a valid `schema.yaml` file and default template files.

#### Scenario: Create schema with valid name
- **WHEN** user runs `openspec schema init my-workflow`
- **THEN** system creates directory `openspec/schemas/my-workflow/`
- **AND** creates `schema.yaml` with name, version, description, and artifacts array
- **AND** creates template files referenced by artifacts
- **AND** displays success message with created path

#### Scenario: Reject invalid schema name
- **WHEN** user runs `openspec schema init "My Workflow"` (contains space)
- **THEN** system displays error about invalid schema name
- **AND** suggests using kebab-case format
- **AND** exits with non-zero code

#### Scenario: Schema name already exists
- **WHEN** user runs `openspec schema init existing-schema` and `openspec/schemas/existing-schema/` already exists
- **THEN** system displays error that schema already exists
- **AND** suggests using `--force` to overwrite or `schema fork` to copy
- **AND** exits with non-zero code

### Requirement: Schema init supports interactive mode
The CLI SHALL prompt for schema configuration when run in an interactive terminal without explicit flags.

#### Scenario: Interactive prompts for description
- **WHEN** user runs `openspec schema init my-workflow` in an interactive terminal
- **THEN** system prompts for schema description
- **AND** uses provided description in generated `schema.yaml`

#### Scenario: Interactive prompts for artifact selection
- **WHEN** user runs `openspec schema init my-workflow` in an interactive terminal
- **THEN** system displays multi-select prompt with common artifacts (proposal, specs, design, tasks)
- **AND** each option includes a brief description
- **AND** uses selected artifacts in generated `schema.yaml`

#### Scenario: Non-interactive mode with flags
- **WHEN** user runs `openspec schema init my-workflow --description "My workflow" --artifacts proposal,tasks`
- **THEN** system creates schema without prompting
- **AND** uses flag values for configuration

### Requirement: Schema init supports setting project default
The CLI SHALL offer to set the newly created schema as the project default.

#### Scenario: Set as default interactively
- **WHEN** user runs `openspec schema init my-workflow` in interactive mode
- **AND** user confirms setting as default
- **THEN** system updates `openspec/config.yaml` with `defaultSchema: my-workflow`

#### Scenario: Set as default via flag
- **WHEN** user runs `openspec schema init my-workflow --default`
- **THEN** system creates schema and updates `openspec/config.yaml` with `defaultSchema: my-workflow`

#### Scenario: Skip setting default
- **WHEN** user runs `openspec schema init my-workflow --no-default`
- **THEN** system creates schema without modifying `openspec/config.yaml`

### Requirement: Schema init outputs JSON format
The CLI SHALL support `--json` flag for machine-readable output.

#### Scenario: JSON output on success
- **WHEN** user runs `openspec schema init my-workflow --json --description "Test" --artifacts proposal`
- **THEN** system outputs JSON with `created: true`, `path`, and `schema` fields
- **AND** does not display interactive prompts or spinners

#### Scenario: JSON output on error
- **WHEN** user runs `openspec schema init "invalid name" --json`
- **THEN** system outputs JSON with `error` field describing the issue
- **AND** exits with non-zero code
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
## ADDED Requirements

### Requirement: Schema validate checks schema structure
The CLI SHALL provide an `openspec schema validate [name]` command that validates schema configuration and reports errors.

#### Scenario: Validate specific schema
- **WHEN** user runs `openspec schema validate my-workflow`
- **THEN** system locates schema using resolution order
- **AND** validates `schema.yaml` against the schema Zod type
- **AND** displays validation result (valid or list of errors)

#### Scenario: Validate all project schemas
- **WHEN** user runs `openspec schema validate` without a name
- **THEN** system validates all schemas in `openspec/schemas/`
- **AND** displays results for each schema
- **AND** exits with non-zero code if any schema is invalid

#### Scenario: Schema not found
- **WHEN** user runs `openspec schema validate nonexistent`
- **THEN** system displays error that schema was not found
- **AND** exits with non-zero code

### Requirement: Schema validate checks YAML syntax
The CLI SHALL report YAML parsing errors with line numbers when possible.

#### Scenario: Invalid YAML syntax
- **WHEN** user runs `openspec schema validate my-workflow` and `schema.yaml` has syntax errors
- **THEN** system displays YAML parse error with line number
- **AND** exits with non-zero code

#### Scenario: Valid YAML but missing required fields
- **WHEN** `schema.yaml` is valid YAML but missing `name` field
- **THEN** system displays Zod validation error for missing required field
- **AND** identifies the specific missing field

### Requirement: Schema validate checks template existence
The CLI SHALL verify that all template files referenced by artifacts exist.

#### Scenario: Missing template file
- **WHEN** artifact references `template: proposal.md` but file doesn't exist in schema directory
- **THEN** system reports error: "Template file 'proposal.md' not found for artifact 'proposal'"
- **AND** exits with non-zero code

#### Scenario: All templates exist
- **WHEN** all artifact templates exist
- **THEN** system reports that templates are valid
- **AND** template existence is included in validation summary

### Requirement: Schema validate checks dependency graph
The CLI SHALL verify that artifact dependencies form a valid directed acyclic graph.

#### Scenario: Valid dependency graph
- **WHEN** artifact dependencies form a valid DAG (e.g., tasks → specs → proposal)
- **THEN** system reports dependency graph is valid

#### Scenario: Circular dependency detected
- **WHEN** artifact A requires B and artifact B requires A
- **THEN** system reports circular dependency error
- **AND** identifies the artifacts involved in the cycle
- **AND** exits with non-zero code

#### Scenario: Unknown dependency reference
- **WHEN** artifact requires `nonexistent-artifact`
- **THEN** system reports error: "Artifact 'x' requires unknown artifact 'nonexistent-artifact'"
- **AND** exits with non-zero code

### Requirement: Schema validate outputs JSON format
The CLI SHALL support `--json` flag for machine-readable validation results.

#### Scenario: JSON output for valid schema
- **WHEN** user runs `openspec schema validate my-workflow --json` and schema is valid
- **THEN** system outputs JSON with `valid: true`, `name`, and `path` fields

#### Scenario: JSON output for invalid schema
- **WHEN** user runs `openspec schema validate my-workflow --json` and schema has errors
- **THEN** system outputs JSON with `valid: false` and `issues` array
- **AND** each issue includes `level`, `path`, and `message` fields
- **AND** format matches existing `openspec validate` output structure

### Requirement: Schema validate supports verbose mode
The CLI SHALL support `--verbose` flag for detailed validation information.

#### Scenario: Verbose output shows all checks
- **WHEN** user runs `openspec schema validate my-workflow --verbose`
- **THEN** system displays each validation check as it runs
- **AND** shows pass/fail status for: YAML parsing, Zod validation, template existence, dependency graph
Loading
Loading