Files
Tabish Bidiwale 971f8ca4a3 feat(cli): add openspec config command for global configuration management (#382)
* feat(cli): add openspec config command for global configuration management

Implements the `openspec config` command with subcommands:
- `path`: Show config file location
- `list [--json]`: Show all current settings
- `get <key>`: Get a specific value (raw output for scripting)
- `set <key> <value> [--string]`: Set a value with auto type coercion
- `unset <key>`: Remove a key (revert to default)
- `reset --all [-y]`: Reset configuration to defaults
- `edit`: Open config in $EDITOR/$VISUAL

Key features:
- Dot notation for nested key access (e.g., featureFlags.someFlag)
- Auto type coercion (true/false → boolean, numbers → number)
- --string flag to force string storage
- Zod schema validation with unknown field passthrough
- Reserved --scope flag for future project-local config
- Windows-compatible editor spawning with proper path quoting
- Shell completion registry integration

* test(config): add additional unit tests for validation and coercion

- Add tests for unknown fields with various types
- Add test to verify error message path for featureFlags
- Add test for number values rejection in featureFlags
- Add config set simulation tests to verify full coerce → set → validate flow

* fix(config): avoid shell parsing in config edit to handle paths with spaces

Use spawn with shell: false and pass configPath as an argument instead
of building a shell command string. This correctly handles spaces in
both the EDITOR path and config file path on all platforms.

* chore(openspec): archive add-config-command and create cli-config spec

Move completed change to archive and apply spec deltas to create
the cli-config specification documenting the config command interface.

* Validate config keys on set
2025-12-22 18:29:25 +11:00

7.0 KiB

cli-config Specification

Purpose

Provide a user-friendly CLI interface for viewing and modifying global OpenSpec configuration settings without manually editing JSON files.

Requirements

Requirement: Command Structure

The config command SHALL provide subcommands for all configuration operations.

Scenario: Available subcommands

  • WHEN user executes openspec config --help
  • THEN display available subcommands:
    • path - Show config file location
    • list - Show all current settings
    • get <key> - Get a specific value
    • set <key> <value> - Set a value
    • unset <key> - Remove a key (revert to default)
    • reset - Reset configuration to defaults
    • edit - Open config in editor

Requirement: Config Path

The config command SHALL display the config file location.

Scenario: Show config path

  • WHEN user executes openspec config path
  • THEN print the absolute path to the config file
  • AND exit with code 0

Requirement: Config List

The config command SHALL display all current configuration values.

Scenario: List config in human-readable format

  • WHEN user executes openspec config list
  • THEN display all config values in YAML-like format
  • AND show nested objects with indentation

Scenario: List config as JSON

  • WHEN user executes openspec config list --json
  • THEN output the complete config as valid JSON
  • AND output only JSON (no additional text)

Requirement: Config Get

The config command SHALL retrieve specific configuration values.

Scenario: Get top-level key

  • WHEN user executes openspec config get <key> with a valid top-level key
  • THEN print the raw value only (no labels or formatting)
  • AND exit with code 0

Scenario: Get nested key with dot notation

  • WHEN user executes openspec config get featureFlags.someFlag
  • THEN traverse the nested structure using dot notation
  • AND print the value at that path

Scenario: Get non-existent key

  • WHEN user executes openspec config get <key> with a key that does not exist
  • THEN print nothing (empty output)
  • AND exit with code 1

Scenario: Get object value

  • WHEN user executes openspec config get <key> where the value is an object
  • THEN print the object as JSON

Requirement: Config Set

The config command SHALL set configuration values with automatic type coercion.

Scenario: Set string value

  • WHEN user executes openspec config set <key> <value>
  • AND value does not match boolean or number patterns
  • THEN store value as a string
  • AND display confirmation message

Scenario: Set boolean value

  • WHEN user executes openspec config set <key> true or openspec config set <key> false
  • THEN store value as boolean (not string)
  • AND display confirmation message

Scenario: Set numeric value

  • WHEN user executes openspec config set <key> <value>
  • AND value is a valid number (integer or float)
  • THEN store value as number (not string)

Scenario: Force string with --string flag

  • WHEN user executes openspec config set <key> <value> --string
  • THEN store value as string regardless of content
  • AND this allows storing literal "true" or "123" as strings

Scenario: Set nested key

  • WHEN user executes openspec config set featureFlags.newFlag true
  • THEN create intermediate objects if they don't exist
  • AND set the value at the nested path

Requirement: Config Unset

The config command SHALL remove configuration overrides.

Scenario: Unset existing key

  • WHEN user executes openspec config unset <key>
  • AND the key exists in the config
  • THEN remove the key from the config file
  • AND the value reverts to its default
  • AND display confirmation message

Scenario: Unset non-existent key

  • WHEN user executes openspec config unset <key>
  • AND the key does not exist in the config
  • THEN display message indicating key was not set
  • AND exit with code 0

Requirement: Config Reset

The config command SHALL reset configuration to defaults.

Scenario: Reset all with confirmation

  • WHEN user executes openspec config reset --all
  • THEN prompt for confirmation before proceeding
  • AND if confirmed, delete the config file or reset to defaults
  • AND display confirmation message

Scenario: Reset all with -y flag

  • WHEN user executes openspec config reset --all -y
  • THEN reset without prompting for confirmation

Scenario: Reset without --all flag

  • WHEN user executes openspec config reset without --all
  • THEN display error indicating --all is required
  • AND exit with code 1

Requirement: Config Edit

The config command SHALL open the config file in the user's editor.

Scenario: Open editor successfully

  • WHEN user executes openspec config edit
  • AND $EDITOR or $VISUAL environment variable is set
  • THEN open the config file in that editor
  • AND create the config file with defaults if it doesn't exist
  • AND wait for the editor to close before returning

Scenario: No editor configured

  • WHEN user executes openspec config edit
  • AND neither $EDITOR nor $VISUAL is set
  • THEN display error message suggesting to set $EDITOR
  • AND exit with code 1

Requirement: Key Naming Convention

The config command SHALL use camelCase keys matching the JSON structure.

Scenario: Keys match JSON structure

  • WHEN accessing configuration keys via CLI
  • THEN use camelCase matching the actual JSON property names
  • AND support dot notation for nested access (e.g., featureFlags.someFlag)

Requirement: Schema Validation

The config command SHALL validate configuration writes against the config schema using zod, while rejecting unknown keys for config set unless explicitly overridden.

Scenario: Unknown key rejected by default

  • WHEN user executes openspec config set someFutureKey 123
  • THEN display a descriptive error message indicating the key is invalid
  • AND do not modify the config file
  • AND exit with code 1

Scenario: Unknown key accepted with override

  • WHEN user executes openspec config set someFutureKey 123 --allow-unknown
  • THEN the value is saved successfully
  • AND exit with code 0

Scenario: Invalid feature flag value rejected

  • WHEN user executes openspec config set featureFlags.someFlag notABoolean
  • THEN display a descriptive error message
  • AND do not modify the config file
  • AND exit with code 1

Requirement: Reserved Scope Flag

The config command SHALL reserve the --scope flag for future extensibility.

Scenario: Scope flag defaults to global

  • WHEN user executes any config command without --scope
  • THEN operate on global configuration (default behavior)

Scenario: Project scope not yet implemented

  • WHEN user executes openspec config --scope project <subcommand>
  • THEN display error message: "Project-local config is not yet implemented"
  • AND exit with code 1