diff --git a/README.md b/README.md index b8af8ca15..7329d0726 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Our philosophy: > [!TIP] > **New workflow now available!** We've rebuilt OpenSpec with a new artifact-guided workflow. > -> Run `/opsx:propose "your idea"` to get started. → [Learn more here](docs/opsx.md) +> Run `/openspec:propose "your idea"` to get started. → [Learn more here](docs/opsx.md)

Follow @0xTab on X for updates · Join the OpenSpec Discord for help and questions. @@ -46,12 +46,12 @@ Our philosophy: Using OpenSpec in a team? [Email here](mailto:teams@openspec.dev) for access to our Slack channel. - + ## See it in action ```text -You: /opsx:propose add-dark-mode +You: /openspec:propose add-dark-mode AI: Created openspec/changes/add-dark-mode/ ✓ proposal.md — why we're doing this, what's changing ✓ specs/ — requirements and scenarios @@ -59,7 +59,7 @@ AI: Created openspec/changes/add-dark-mode/ ✓ tasks.md — implementation checklist Ready for implementation! -You: /opsx:apply +You: /openspec:apply AI: Implementing tasks... ✓ 1.1 Add theme context provider ✓ 1.2 Create toggle component @@ -67,7 +67,7 @@ AI: Implementing tasks... ✓ 2.2 Wire up localStorage All tasks complete! -You: /opsx:archive +You: /openspec:archive AI: Archived to openspec/changes/archive/2025-01-23-add-dark-mode/ Specs updated. Ready for the next feature. ``` @@ -98,9 +98,9 @@ cd your-project openspec init ``` -Now tell your AI: `/opsx:propose ` +Now tell your AI: `/openspec:propose ` -If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:sync`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`. +If you want the expanded workflow (`/openspec:new`, `/openspec:continue`, `/openspec:ff`, `/openspec:verify`, `/openspec:sync`, `/openspec:bulk-archive`, `/openspec:onboard`), select it with `openspec config profile` and apply with `openspec update`. > [!NOTE] > Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 20+ tools and growing. diff --git a/docs/cli.md b/docs/cli.md index ebbb847eb..88a56782e 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -1,6 +1,6 @@ # CLI Reference -The OpenSpec CLI (`openspec`) provides terminal commands for project setup, validation, status inspection, and management. These commands complement the AI slash commands (like `/opsx:propose`) documented in [Commands](commands.md). +The OpenSpec CLI (`openspec`) provides terminal commands for project setup, validation, status inspection, and management. These commands complement the AI slash commands (like `/openspec:propose`) documented in [Commands](commands.md). ## Summary @@ -928,7 +928,7 @@ openspec completion uninstall ## Related Documentation -- [Commands](commands.md) - AI slash commands (`/opsx:propose`, `/opsx:apply`, etc.) +- [Commands](commands.md) - AI slash commands (`/openspec:propose`, `/openspec:apply`, etc.) - [Workflows](workflows.md) - Common patterns and when to use each command - [Customization](customization.md) - Create custom schemas and templates - [Getting Started](getting-started.md) - First-time setup guide diff --git a/docs/commands.md b/docs/commands.md index fd4bb7fe1..1bc9a7eb3 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -10,22 +10,22 @@ For workflow patterns and when to use each command, see [Workflows](workflows.md | Command | Purpose | |---------|---------| -| `/opsx:propose` | Create a change and generate planning artifacts in one step | -| `/opsx:explore` | Think through ideas before committing to a change | -| `/opsx:apply` | Implement tasks from the change | -| `/opsx:archive` | Archive a completed change | +| `/openspec:propose` | Create a change and generate planning artifacts in one step | +| `/openspec:explore` | Think through ideas before committing to a change | +| `/openspec:apply` | Implement tasks from the change | +| `/openspec:archive` | Archive a completed change | ### Expanded Workflow Commands (custom workflow selection) | Command | Purpose | |---------|---------| -| `/opsx:new` | Start a new change scaffold | -| `/opsx:continue` | Create the next artifact based on dependencies | -| `/opsx:ff` | Fast-forward: create all planning artifacts at once | -| `/opsx:verify` | Validate implementation matches artifacts | -| `/opsx:sync` | Merge delta specs into main specs | -| `/opsx:bulk-archive` | Archive multiple changes at once | -| `/opsx:onboard` | Guided tutorial through the complete workflow | +| `/openspec:new` | Start a new change scaffold | +| `/openspec:continue` | Create the next artifact based on dependencies | +| `/openspec:ff` | Fast-forward: create all planning artifacts at once | +| `/openspec:verify` | Validate implementation matches artifacts | +| `/openspec:sync` | Merge delta specs into main specs | +| `/openspec:bulk-archive` | Archive multiple changes at once | +| `/openspec:onboard` | Guided tutorial through the complete workflow | The default global profile is `core`. To enable expanded workflow commands, run `openspec config profile`, select workflows, then run `openspec update` in your project. @@ -33,13 +33,13 @@ The default global profile is `core`. To enable expanded workflow commands, run ## Command Reference -### `/opsx:propose` +### `/openspec:propose` Create a new change and generate planning artifacts in one step. This is the default start command in the `core` profile. **Syntax:** ```text -/opsx:propose [change-name-or-description] +/openspec:propose [change-name-or-description] ``` **Arguments:** @@ -50,33 +50,33 @@ Create a new change and generate planning artifacts in one step. This is the def **What it does:** - Creates `openspec/changes//` - Generates artifacts needed before implementation (for `spec-driven`: proposal, specs, design, tasks) -- Stops when the change is ready for `/opsx:apply` +- Stops when the change is ready for `/openspec:apply` **Example:** ```text -You: /opsx:propose add-dark-mode +You: /openspec:propose add-dark-mode AI: Created openspec/changes/add-dark-mode/ ✓ proposal.md ✓ specs/ui/spec.md ✓ design.md ✓ tasks.md - Ready for implementation. Run /opsx:apply. + Ready for implementation. Run /openspec:apply. ``` **Tips:** - Use this for the fastest end-to-end path -- If you want step-by-step artifact control, enable expanded workflows and use `/opsx:new` + `/opsx:continue` +- If you want step-by-step artifact control, enable expanded workflows and use `/openspec:new` + `/openspec:continue` --- -### `/opsx:explore` +### `/openspec:explore` Think through ideas, investigate problems, and clarify requirements before committing to a change. **Syntax:** ``` -/opsx:explore [topic] +/openspec:explore [topic] ``` **Arguments:** @@ -89,11 +89,11 @@ Think through ideas, investigate problems, and clarify requirements before commi - Investigates the codebase to answer questions - Compares options and approaches - Creates visual diagrams to clarify thinking -- Can transition to `/opsx:propose` (default) or `/opsx:new` (expanded workflow) when insights crystallize +- Can transition to `/openspec:propose` (default) or `/openspec:new` (expanded workflow) when insights crystallize **Example:** ```text -You: /opsx:explore +You: /openspec:explore AI: What would you like to explore? @@ -113,7 +113,7 @@ AI: Let me investigate your current auth setup... You: Let's go with JWT. Can we start a change for that? -AI: Ready when you are. Run /opsx:propose add-jwt-auth to begin. +AI: Ready when you are. Run /openspec:propose add-jwt-auth to begin. ``` **Tips:** @@ -124,15 +124,15 @@ AI: Ready when you are. Run /opsx:propose add-jwt-auth to begin. --- -### `/opsx:new` +### `/openspec:new` -Start a new change scaffold. Creates the change folder and waits for you to generate artifacts with `/opsx:continue` or `/opsx:ff`. +Start a new change scaffold. Creates the change folder and waits for you to generate artifacts with `/openspec:continue` or `/openspec:ff`. This command is part of the expanded workflow set (not included in the default `core` profile). **Syntax:** ``` -/opsx:new [change-name] [--schema ] +/openspec:new [change-name] [--schema ] ``` **Arguments:** @@ -155,13 +155,13 @@ openspec/changes// **Example:** ``` -You: /opsx:new add-dark-mode +You: /openspec:new add-dark-mode AI: Created openspec/changes/add-dark-mode/ Schema: spec-driven Ready to create: proposal - Use /opsx:continue to create it, or /opsx:ff to create all artifacts. + Use /openspec:continue to create it, or /openspec:ff to create all artifacts. ``` **Tips:** @@ -171,13 +171,13 @@ AI: Created openspec/changes/add-dark-mode/ --- -### `/opsx:continue` +### `/openspec:continue` Create the next artifact in the dependency chain. Creates one artifact at a time for incremental progress. **Syntax:** ``` -/opsx:continue [change-name] +/openspec:continue [change-name] ``` **Arguments:** @@ -194,7 +194,7 @@ Create the next artifact in the dependency chain. Creates one artifact at a time **Example:** ``` -You: /opsx:continue +You: /openspec:continue AI: Change: add-dark-mode @@ -211,7 +211,7 @@ AI: Change: add-dark-mode ✓ Created openspec/changes/add-dark-mode/specs/ui/spec.md Now available: tasks - Run /opsx:continue to create the next artifact. + Run /openspec:continue to create the next artifact. ``` **Tips:** @@ -222,13 +222,13 @@ AI: Change: add-dark-mode --- -### `/opsx:ff` +### `/openspec:ff` Fast-forward through artifact creation. Creates all planning artifacts at once. **Syntax:** ``` -/opsx:ff [change-name] +/openspec:ff [change-name] ``` **Arguments:** @@ -244,7 +244,7 @@ Fast-forward through artifact creation. Creates all planning artifacts at once. **Example:** ``` -You: /opsx:ff add-dark-mode +You: /openspec:ff add-dark-mode AI: Fast-forwarding add-dark-mode... @@ -254,24 +254,24 @@ AI: Fast-forwarding add-dark-mode... ✓ Creating tasks.md All planning artifacts complete! - Ready for implementation. Run /opsx:apply to begin. + Ready for implementation. Run /openspec:apply to begin. ``` **Tips:** - Use when you have a clear picture of what you're building -- Faster than `/opsx:continue` for straightforward changes +- Faster than `/openspec:continue` for straightforward changes - You can still edit artifacts afterward - Good for small to medium features --- -### `/opsx:apply` +### `/openspec:apply` Implement tasks from the change. Works through the task list, writing code and checking off items. **Syntax:** ``` -/opsx:apply [change-name] +/openspec:apply [change-name] ``` **Arguments:** @@ -287,7 +287,7 @@ Implement tasks from the change. Works through the task list, writing code and c **Example:** ``` -You: /opsx:apply +You: /openspec:apply AI: Implementing add-dark-mode... @@ -315,13 +315,13 @@ AI: Implementing add-dark-mode... --- -### `/opsx:verify` +### `/openspec:verify` Validate that implementation matches your change artifacts. Checks completeness, correctness, and coherence. **Syntax:** ``` -/opsx:verify [change-name] +/openspec:verify [change-name] ``` **Arguments:** @@ -345,7 +345,7 @@ Validate that implementation matches your change artifacts. Checks completeness, **Example:** ``` -You: /opsx:verify +You: /openspec:verify AI: Verifying add-dark-mode... @@ -383,13 +383,13 @@ AI: Verifying add-dark-mode... --- -### `/opsx:sync` +### `/openspec:sync` **Optional command.** Merge delta specs from a change into main specs. Archive will prompt to sync if needed, so you typically don't need to run this manually. **Syntax:** ``` -/opsx:sync [change-name] +/openspec:sync [change-name] ``` **Arguments:** @@ -406,7 +406,7 @@ AI: Verifying add-dark-mode... **Example:** ```text -You: /opsx:sync +You: /openspec:sync AI: Syncing add-dark-mode delta specs... @@ -420,7 +420,7 @@ AI: Syncing add-dark-mode delta specs... Merging... ✓ openspec/specs/ui/spec.md updated - Change remains active. Run /opsx:archive when ready to complete. + Change remains active. Run /openspec:archive when ready to complete. ``` **When to use manually:** @@ -440,13 +440,13 @@ AI: Syncing add-dark-mode delta specs... --- -### `/opsx:archive` +### `/openspec:archive` Archive a completed change. Finalizes the change and moves it to the archive folder. **Syntax:** ``` -/opsx:archive [change-name] +/openspec:archive [change-name] ``` **Arguments:** @@ -463,7 +463,7 @@ Archive a completed change. Finalizes the change and moves it to the archive fol **Example:** ```text -You: /opsx:archive +You: /openspec:archive AI: Archiving add-dark-mode... @@ -488,17 +488,17 @@ AI: ✓ Synced specs to openspec/specs/ui/spec.md - Archive won't block on incomplete tasks, but will warn - Delta specs can be synced during archive or beforehand - Archived changes are preserved for history -- Use `/opsx:verify` first to catch issues +- Use `/openspec:verify` first to catch issues --- -### `/opsx:bulk-archive` +### `/openspec:bulk-archive` Archive multiple completed changes at once. Handles spec conflicts between changes. **Syntax:** ``` -/opsx:bulk-archive [change-names...] +/openspec:bulk-archive [change-names...] ``` **Arguments:** @@ -515,7 +515,7 @@ Archive multiple completed changes at once. Handles spec conflicts between chang **Example:** ``` -You: /opsx:bulk-archive +You: /openspec:bulk-archive AI: Found 3 completed changes: - add-dark-mode (8/8 tasks complete) @@ -548,13 +548,13 @@ AI: ✓ Archived add-dark-mode --- -### `/opsx:onboard` +### `/openspec:onboard` Guided onboarding through the complete OpenSpec workflow. An interactive tutorial using your actual codebase. **Syntax:** ``` -/opsx:onboard +/openspec:onboard ``` **What it does:** @@ -568,19 +568,19 @@ Guided onboarding through the complete OpenSpec workflow. An interactive tutoria **Phases:** 1. Welcome and codebase analysis 2. Finding an improvement opportunity -3. Creating a change (`/opsx:new`) +3. Creating a change (`/openspec:new`) 4. Writing the proposal 5. Creating specs 6. Writing the design 7. Creating tasks -8. Implementing tasks (`/opsx:apply`) +8. Implementing tasks (`/openspec:apply`) 9. Verifying implementation 10. Archiving the change 11. Summary and next steps **Example:** ``` -You: /opsx:onboard +You: /openspec:onboard AI: Welcome to OpenSpec! @@ -614,11 +614,11 @@ Different AI tools use slightly different command syntax. Use the format that ma | Tool | Syntax Example | |------|----------------| -| Claude Code | `/opsx:propose`, `/opsx:apply` | -| Cursor | `/opsx-propose`, `/opsx-apply` | -| Windsurf | `/opsx-propose`, `/opsx-apply` | -| Copilot (IDE) | `/opsx-propose`, `/opsx-apply` | -| Trae | Skill-based invocations such as `/openspec-propose`, `/openspec-apply-change` (no generated `opsx-*` command files) | +| Claude Code | `/openspec:propose`, `/openspec:apply` | +| Cursor | `/openspec-propose`, `/openspec-apply` | +| Windsurf | `/openspec-propose`, `/openspec-apply` | +| Copilot (IDE) | `/openspec-propose`, `/openspec-apply` | +| Trae | Skill-based invocations such as `/openspec-propose`, `/openspec-apply-change` (no generated `openspec-*` command files) | The intent is the same across tools, but how commands are surfaced can differ by integration. @@ -653,7 +653,7 @@ Legacy changes can be continued with OPSX commands. The artifact structure is co The command couldn't identify which change to work on. **Solutions:** -- Specify the change name explicitly: `/opsx:apply add-dark-mode` +- Specify the change name explicitly: `/openspec:apply add-dark-mode` - Check that the change folder exists: `openspec list` - Verify you're in the right project directory @@ -693,7 +693,7 @@ The AI creates incomplete or incorrect artifacts. - Add project context in `openspec/config.yaml` - Add per-artifact rules for specific guidance - Provide more detail in your change description -- Use `/opsx:continue` instead of `/opsx:ff` for more control +- Use `/openspec:continue` instead of `/openspec:ff` for more control --- diff --git a/docs/concepts.md b/docs/concepts.md index 96cdd9d27..f40f2999e 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -562,27 +562,27 @@ openspec/ │ OPENSPEC FLOW │ │ │ │ ┌────────────────┐ │ -│ │ 1. START │ /opsx:propose (core) or /opsx:new (expanded) │ +│ │ 1. START │ /openspec:propose (core) or /openspec:new (expanded) │ │ │ CHANGE │ │ │ └───────┬────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────┐ │ -│ │ 2. CREATE │ /opsx:ff or /opsx:continue (expanded workflow) │ +│ │ 2. CREATE │ /openspec:ff or /openspec:continue (expanded workflow) │ │ │ ARTIFACTS │ Creates proposal → specs → design → tasks │ │ │ │ (based on schema dependencies) │ │ └───────┬────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────┐ │ -│ │ 3. IMPLEMENT │ /opsx:apply │ +│ │ 3. IMPLEMENT │ /openspec:apply │ │ │ TASKS │ Work through tasks, checking them off │ │ │ │◄──── Update artifacts as you learn │ │ └───────┬────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────┐ │ -│ │ 4. VERIFY │ /opsx:verify (optional) │ +│ │ 4. VERIFY │ /openspec:verify (optional) │ │ │ WORK │ Check implementation matches specs │ │ └───────┬────────┘ │ │ │ │ diff --git a/docs/getting-started.md b/docs/getting-started.md index 6f4b88862..fa92b06c9 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -9,13 +9,13 @@ OpenSpec helps you and your AI coding assistant agree on what to build before an **Default quick path (core profile):** ```text -/opsx:propose ──► /opsx:apply ──► /opsx:archive +/openspec:propose ──► /openspec:apply ──► /openspec:archive ``` **Expanded path (custom workflow selection):** ```text -/opsx:new ──► /opsx:ff or /opsx:continue ──► /opsx:apply ──► /opsx:verify ──► /opsx:archive +/openspec:new ──► /openspec:ff or /openspec:continue ──► /openspec:apply ──► /openspec:verify ──► /openspec:archive ``` The default global profile is `core`, which includes `propose`, `explore`, `apply`, and `archive`. You can enable the expanded workflow commands with `openspec config profile` and then `openspec update`. @@ -123,7 +123,7 @@ Let's walk through adding dark mode to an application. ### 1. Start the Change (Default) ```text -You: /opsx:propose add-dark-mode +You: /openspec:propose add-dark-mode AI: Created openspec/changes/add-dark-mode/ ✓ proposal.md — why we're doing this, what's changing @@ -133,7 +133,7 @@ AI: Created openspec/changes/add-dark-mode/ Ready for implementation! ``` -If you've enabled the expanded workflow profile, you can also do this as two steps: `/opsx:new` then `/opsx:ff` (or `/opsx:continue` incrementally). +If you've enabled the expanded workflow profile, you can also do this as two steps: `/openspec:new` then `/openspec:ff` (or `/openspec:continue` incrementally). ### 2. What Gets Created @@ -201,7 +201,7 @@ The system SHALL allow users to choose between light and dark themes. ### 3. Implement ``` -You: /opsx:apply +You: /openspec:apply AI: Working through tasks... ✓ 1.1 Created ThemeContext with light/dark state @@ -217,7 +217,7 @@ During implementation, if you discover the design needs adjustment, just update ### 4. Archive ``` -You: /opsx:archive +You: /openspec:archive AI: Archiving add-dark-mode... ✓ Merged specs into openspec/specs/ui/spec.md diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 5091ce438..2258aaca8 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -8,7 +8,7 @@ OPSX replaces the old phase-locked workflow with a fluid, action-based approach. | Aspect | Legacy | OPSX | |--------|--------|------| -| **Commands** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` | Default: `/opsx:propose`, `/opsx:apply`, `/opsx:archive` (expanded workflow commands optional) | +| **Commands** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` | Default: `/openspec:propose`, `/openspec:apply`, `/openspec:archive` (expanded workflow commands optional) | | **Workflow** | Create all artifacts at once | Create incrementally or all at once—your choice | | **Going back** | Awkward phase gates | Natural—update any artifact anytime | | **Customization** | Fixed structure | Schema-driven, fully hackable | @@ -284,22 +284,22 @@ Command availability is profile-dependent: | Command | Purpose | |---------|---------| -| `/opsx:propose` | Create a change and generate planning artifacts in one step | -| `/opsx:explore` | Think through ideas with no structure | -| `/opsx:apply` | Implement tasks from tasks.md | -| `/opsx:archive` | Finalize and archive the change | +| `/openspec:propose` | Create a change and generate planning artifacts in one step | +| `/openspec:explore` | Think through ideas with no structure | +| `/openspec:apply` | Implement tasks from tasks.md | +| `/openspec:archive` | Finalize and archive the change | **Expanded workflow (custom selection):** | Command | Purpose | |---------|---------| -| `/opsx:new` | Start a new change scaffold | -| `/opsx:continue` | Create the next artifact (one at a time) | -| `/opsx:ff` | Fast-forward—create planning artifacts at once | -| `/opsx:verify` | Validate implementation matches specs | -| `/opsx:sync` | Preview/spec-merge without archiving | -| `/opsx:bulk-archive` | Archive multiple changes at once | -| `/opsx:onboard` | Guided end-to-end onboarding workflow | +| `/openspec:new` | Start a new change scaffold | +| `/openspec:continue` | Create the next artifact (one at a time) | +| `/openspec:ff` | Fast-forward—create planning artifacts at once | +| `/openspec:verify` | Validate implementation matches specs | +| `/openspec:sync` | Preview/spec-merge without archiving | +| `/openspec:bulk-archive` | Archive multiple changes at once | +| `/openspec:onboard` | Guided end-to-end onboarding workflow | Enable expanded commands with `openspec config profile`, then run `openspec update`. @@ -307,9 +307,9 @@ Enable expanded commands with `openspec config profile`, then run `openspec upda | Legacy | OPSX Equivalent | |--------|-----------------| -| `/openspec:proposal` | `/opsx:propose` (default) or `/opsx:new` then `/opsx:ff` (expanded) | -| `/openspec:apply` | `/opsx:apply` | -| `/openspec:archive` | `/opsx:archive` | +| `/openspec:proposal` | `/openspec:propose` (default) or `/openspec:new` then `/openspec:ff` (expanded) | +| `/openspec:apply` | `/openspec:apply` | +| `/openspec:archive` | `/openspec:archive` | ### New Capabilities @@ -317,13 +317,13 @@ These capabilities are part of the expanded workflow command set. **Granular artifact creation:** ``` -/opsx:continue +/openspec:continue ``` Creates one artifact at a time based on dependencies. Use this when you want to review each step. **Exploration mode:** ``` -/opsx:explore +/openspec:explore ``` Think through ideas with a partner before committing to a change. @@ -381,7 +381,7 @@ Artifacts form a directed graph. Dependencies are enablers, not gates: specs, design) ``` -When you run `/opsx:continue`, it checks what's ready and offers the next artifact. You can also create multiple ready artifacts in any order. +When you run `/openspec:continue`, it checks what's ready and offers the next artifact. You can also create multiple ready artifacts in any order. ### Skills vs Commands @@ -416,7 +416,7 @@ Your in-progress changes work seamlessly with OPSX commands. **Have an active change from the legacy workflow?** ``` -/opsx:apply add-my-feature +/openspec:apply add-my-feature ``` OPSX reads the existing artifacts and continues from where you left off. @@ -424,7 +424,7 @@ OPSX reads the existing artifacts and continues from where you left off. **Want to add more artifacts to an existing change?** ``` -/opsx:continue add-my-feature +/openspec:continue add-my-feature ``` Shows what's ready to create based on what already exists. @@ -576,14 +576,14 @@ project/ ### Command Cheatsheet ```text -/opsx:propose Start quickly (default core profile) -/opsx:apply Implement tasks -/opsx:archive Finish and archive +/openspec:propose Start quickly (default core profile) +/openspec:apply Implement tasks +/openspec:archive Finish and archive # Expanded workflow (if enabled): -/opsx:new Scaffold a change -/opsx:continue Create next artifact -/opsx:ff Create planning artifacts +/openspec:new Scaffold a change +/openspec:continue Create next artifact +/openspec:ff Create planning artifacts ``` --- diff --git a/docs/opsx.md b/docs/opsx.md index 9607b7d06..5a13db95f 100644 --- a/docs/opsx.md +++ b/docs/opsx.md @@ -157,60 +157,60 @@ rules: | Command | What it does | |---------|--------------| -| `/opsx:propose` | Create a change and generate planning artifacts in one step (default quick path) | -| `/opsx:explore` | Think through ideas, investigate problems, clarify requirements | -| `/opsx:new` | Start a new change scaffold (expanded workflow) | -| `/opsx:continue` | Create the next artifact (expanded workflow) | -| `/opsx:ff` | Fast-forward planning artifacts (expanded workflow) | -| `/opsx:apply` | Implement tasks, updating artifacts as needed | -| `/opsx:verify` | Validate implementation against artifacts (expanded workflow) | -| `/opsx:sync` | Sync delta specs to main (expanded workflow, optional) | -| `/opsx:archive` | Archive when done | -| `/opsx:bulk-archive` | Archive multiple completed changes (expanded workflow) | -| `/opsx:onboard` | Guided walkthrough of an end-to-end change (expanded workflow) | +| `/openspec:propose` | Create a change and generate planning artifacts in one step (default quick path) | +| `/openspec:explore` | Think through ideas, investigate problems, clarify requirements | +| `/openspec:new` | Start a new change scaffold (expanded workflow) | +| `/openspec:continue` | Create the next artifact (expanded workflow) | +| `/openspec:ff` | Fast-forward planning artifacts (expanded workflow) | +| `/openspec:apply` | Implement tasks, updating artifacts as needed | +| `/openspec:verify` | Validate implementation against artifacts (expanded workflow) | +| `/openspec:sync` | Sync delta specs to main (expanded workflow, optional) | +| `/openspec:archive` | Archive when done | +| `/openspec:bulk-archive` | Archive multiple completed changes (expanded workflow) | +| `/openspec:onboard` | Guided walkthrough of an end-to-end change (expanded workflow) | ## Usage ### Explore an idea ``` -/opsx:explore +/openspec:explore ``` -Think through ideas, investigate problems, compare options. No structure required - just a thinking partner. When insights crystallize, transition to `/opsx:propose` (default) or `/opsx:new`/`/opsx:ff` (expanded). +Think through ideas, investigate problems, compare options. No structure required - just a thinking partner. When insights crystallize, transition to `/openspec:propose` (default) or `/openspec:new`/`/openspec:ff` (expanded). ### Start a new change ``` -/opsx:propose +/openspec:propose ``` Creates the change and generates planning artifacts needed before implementation. If you've enabled expanded workflows, you can instead use: ```text -/opsx:new # scaffold only -/opsx:continue # create one artifact at a time -/opsx:ff # create all planning artifacts at once +/openspec:new # scaffold only +/openspec:continue # create one artifact at a time +/openspec:ff # create all planning artifacts at once ``` ### Create artifacts ``` -/opsx:continue +/openspec:continue ``` Shows what's ready to create based on dependencies, then creates one artifact. Use repeatedly to build up your change incrementally. ``` -/opsx:ff add-dark-mode +/openspec:ff add-dark-mode ``` Creates all planning artifacts at once. Use when you have a clear picture of what you're building. ### Implement (the fluid part) ``` -/opsx:apply +/openspec:apply ``` -Works through tasks, checking them off as you go. If you're juggling multiple changes, you can run `/opsx:apply `; otherwise it should infer from the conversation and prompt you to choose if it can't tell. +Works through tasks, checking them off as you go. If you're juggling multiple changes, you can run `/openspec:apply `; otherwise it should infer from the conversation and prompt you to choose if it can't tell. ### Finish up ``` -/opsx:archive # Move to archive when done (prompts to sync specs if needed) +/openspec:archive # Move to archive when done (prompts to sync specs if needed) ``` ## When to Update vs. Start Fresh @@ -301,7 +301,7 @@ Think of it like git branches: ## What's Different? -| | Legacy (`/openspec:proposal`) | OPSX (`/opsx:*`) | +| | Legacy (`/openspec:proposal`) | OPSX (`/openspec:*`) | |---|---|---| | **Structure** | One big proposal document | Discrete artifacts with dependencies | | **Workflow** | Linear phases: plan → implement → archive | Fluid actions — do anything anytime | @@ -484,7 +484,7 @@ Artifacts form a directed acyclic graph (DAG). Dependencies are **enablers**, no **OPSX** — agent queries for rich context: ``` - User: "/opsx:continue" + User: "/openspec:continue" │ ▼ ┌──────────────────────────────────────────────────────────────────────────┐ @@ -541,7 +541,7 @@ Artifacts form a directed acyclic graph (DAG). Dependencies are **enablers**, no **OPSX** — natural iteration: ``` - /opsx:new ───► /opsx:continue ───► /opsx:apply ───► /opsx:archive + /openspec:new ───► /openspec:continue ───► /openspec:apply ───► /openspec:archive │ │ │ │ │ ├── "The design is wrong" │ │ │ @@ -550,7 +550,7 @@ Artifacts form a directed acyclic graph (DAG). Dependencies are **enablers**, no │ │ and continue! │ │ │ │ │ ▼ - │ │ /opsx:apply picks up + │ │ /openspec:apply picks up │ │ where you left off │ │ │ └── Creates ONE artifact, shows what's unlocked @@ -646,9 +646,9 @@ openspec schema validate my-workflow ## Tips -- Use `/opsx:explore` to think through an idea before committing to a change -- `/opsx:ff` when you know what you want, `/opsx:continue` when exploring -- During `/opsx:apply`, if something's wrong — fix the artifact, then continue +- Use `/openspec:explore` to think through an idea before committing to a change +- `/openspec:ff` when you know what you want, `/openspec:continue` when exploring +- During `/openspec:apply`, if something's wrong — fix the artifact, then continue - Tasks track progress via checkboxes in `tasks.md` - Check status anytime: `openspec status --change "name"` diff --git a/docs/supported-tools.md b/docs/supported-tools.md index 524e597f2..ba999b7e8 100644 --- a/docs/supported-tools.md +++ b/docs/supported-tools.md @@ -7,7 +7,7 @@ OpenSpec works with many AI coding assistants. When you run `openspec init`, Ope For each selected tool, OpenSpec can install: 1. **Skills** (if delivery includes skills): `.../skills/openspec-*/SKILL.md` -2. **Commands** (if delivery includes commands): tool-specific `opsx-*` command files +2. **Commands** (if delivery includes commands): tool-specific `openspec-*` command files By default, OpenSpec uses the `core` profile, which includes: - `propose` @@ -21,30 +21,30 @@ You can enable expanded workflows (`new`, `continue`, `ff`, `verify`, `sync`, `b | Tool (ID) | Skills path pattern | Command path pattern | |-----------|---------------------|----------------------| -| Amazon Q Developer (`amazon-q`) | `.amazonq/skills/openspec-*/SKILL.md` | `.amazonq/prompts/opsx-.md` | -| Antigravity (`antigravity`) | `.agent/skills/openspec-*/SKILL.md` | `.agent/workflows/opsx-.md` | -| Auggie (`auggie`) | `.augment/skills/openspec-*/SKILL.md` | `.augment/commands/opsx-.md` | +| Amazon Q Developer (`amazon-q`) | `.amazonq/skills/openspec-*/SKILL.md` | `.amazonq/prompts/openspec-.md` | +| Antigravity (`antigravity`) | `.agent/skills/openspec-*/SKILL.md` | `.agent/workflows/openspec-.md` | +| Auggie (`auggie`) | `.augment/skills/openspec-*/SKILL.md` | `.augment/commands/openspec-.md` | | Claude Code (`claude`) | `.claude/skills/openspec-*/SKILL.md` | `.claude/commands/opsx/.md` | -| Cline (`cline`) | `.cline/skills/openspec-*/SKILL.md` | `.clinerules/workflows/opsx-.md` | +| Cline (`cline`) | `.cline/skills/openspec-*/SKILL.md` | `.clinerules/workflows/openspec-.md` | | CodeBuddy (`codebuddy`) | `.codebuddy/skills/openspec-*/SKILL.md` | `.codebuddy/commands/opsx/.md` | -| Codex (`codex`) | `.codex/skills/openspec-*/SKILL.md` | `$CODEX_HOME/prompts/opsx-.md`\* | -| Continue (`continue`) | `.continue/skills/openspec-*/SKILL.md` | `.continue/prompts/opsx-.prompt` | -| CoStrict (`costrict`) | `.cospec/skills/openspec-*/SKILL.md` | `.cospec/openspec/commands/opsx-.md` | +| Codex (`codex`) | `.codex/skills/openspec-*/SKILL.md` | `$CODEX_HOME/prompts/openspec-.md`\* | +| Continue (`continue`) | `.continue/skills/openspec-*/SKILL.md` | `.continue/prompts/openspec-.prompt` | +| CoStrict (`costrict`) | `.cospec/skills/openspec-*/SKILL.md` | `.cospec/openspec/commands/openspec-.md` | | Crush (`crush`) | `.crush/skills/openspec-*/SKILL.md` | `.crush/commands/opsx/.md` | -| Cursor (`cursor`) | `.cursor/skills/openspec-*/SKILL.md` | `.cursor/commands/opsx-.md` | -| Factory Droid (`factory`) | `.factory/skills/openspec-*/SKILL.md` | `.factory/commands/opsx-.md` | +| Cursor (`cursor`) | `.cursor/skills/openspec-*/SKILL.md` | `.cursor/commands/openspec-.md` | +| Factory Droid (`factory`) | `.factory/skills/openspec-*/SKILL.md` | `.factory/commands/openspec-.md` | | Gemini CLI (`gemini`) | `.gemini/skills/openspec-*/SKILL.md` | `.gemini/commands/opsx/.toml` | -| GitHub Copilot (`github-copilot`) | `.github/skills/openspec-*/SKILL.md` | `.github/prompts/opsx-.prompt.md`\*\* | -| iFlow (`iflow`) | `.iflow/skills/openspec-*/SKILL.md` | `.iflow/commands/opsx-.md` | -| Kilo Code (`kilocode`) | `.kilocode/skills/openspec-*/SKILL.md` | `.kilocode/workflows/opsx-.md` | -| Kiro (`kiro`) | `.kiro/skills/openspec-*/SKILL.md` | `.kiro/prompts/opsx-.prompt.md` | -| OpenCode (`opencode`) | `.opencode/skills/openspec-*/SKILL.md` | `.opencode/commands/opsx-.md` | -| Pi (`pi`) | `.pi/skills/openspec-*/SKILL.md` | `.pi/prompts/opsx-.md` | +| GitHub Copilot (`github-copilot`) | `.github/skills/openspec-*/SKILL.md` | `.github/prompts/openspec-.prompt.md`\*\* | +| iFlow (`iflow`) | `.iflow/skills/openspec-*/SKILL.md` | `.iflow/commands/openspec-.md` | +| Kilo Code (`kilocode`) | `.kilocode/skills/openspec-*/SKILL.md` | `.kilocode/workflows/openspec-.md` | +| Kiro (`kiro`) | `.kiro/skills/openspec-*/SKILL.md` | `.kiro/prompts/openspec-.prompt.md` | +| OpenCode (`opencode`) | `.opencode/skills/openspec-*/SKILL.md` | `.opencode/commands/openspec-.md` | +| Pi (`pi`) | `.pi/skills/openspec-*/SKILL.md` | `.pi/prompts/openspec-.md` | | Qoder (`qoder`) | `.qoder/skills/openspec-*/SKILL.md` | `.qoder/commands/opsx/.md` | -| Qwen Code (`qwen`) | `.qwen/skills/openspec-*/SKILL.md` | `.qwen/commands/opsx-.toml` | -| RooCode (`roocode`) | `.roo/skills/openspec-*/SKILL.md` | `.roo/commands/opsx-.md` | +| Qwen Code (`qwen`) | `.qwen/skills/openspec-*/SKILL.md` | `.qwen/commands/openspec-.toml` | +| RooCode (`roocode`) | `.roo/skills/openspec-*/SKILL.md` | `.roo/commands/openspec-.md` | | Trae (`trae`) | `.trae/skills/openspec-*/SKILL.md` | Not generated (no command adapter; use skill-based `/openspec-*` invocations) | -| Windsurf (`windsurf`) | `.windsurf/skills/openspec-*/SKILL.md` | `.windsurf/workflows/opsx-.md` | +| Windsurf (`windsurf`) | `.windsurf/skills/openspec-*/SKILL.md` | `.windsurf/workflows/openspec-.md` | \* Codex commands are installed in the global Codex home (`$CODEX_HOME/prompts/` if set, otherwise `~/.codex/prompts/`), not your project directory. diff --git a/docs/workflows.md b/docs/workflows.md index 6cfd7e063..c63817f8b 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -33,20 +33,20 @@ OPSX (fluid actions): ### Default Quick Path (`core` profile) New installs default to `core`, which provides: -- `/opsx:propose` -- `/opsx:explore` -- `/opsx:apply` -- `/opsx:archive` +- `/openspec:propose` +- `/openspec:explore` +- `/openspec:apply` +- `/openspec:archive` Typical flow: ```text -/opsx:propose ──► /opsx:apply ──► /opsx:archive +/openspec:propose ──► /openspec:apply ──► /openspec:archive ``` ### Expanded/Full Workflow (custom selection) -If you want explicit scaffold-and-build commands (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:sync`, `/opsx:bulk-archive`, `/opsx:onboard`), enable them with: +If you want explicit scaffold-and-build commands (`/openspec:new`, `/openspec:continue`, `/openspec:ff`, `/openspec:verify`, `/openspec:sync`, `/openspec:bulk-archive`, `/openspec:onboard`), enable them with: ```bash openspec config profile @@ -60,18 +60,18 @@ openspec update When you know what you want to build and just need to execute: ```text -/opsx:new ──► /opsx:ff ──► /opsx:apply ──► /opsx:verify ──► /opsx:archive +/openspec:new ──► /openspec:ff ──► /openspec:apply ──► /openspec:verify ──► /openspec:archive ``` **Example conversation:** ```text -You: /opsx:new add-logout-button +You: /openspec:new add-logout-button AI: Created openspec/changes/add-logout-button/ Ready to create: proposal -You: /opsx:ff +You: /openspec:ff AI: Creating all planning artifacts... ✓ proposal.md @@ -80,7 +80,7 @@ AI: Creating all planning artifacts... ✓ tasks.md Ready for implementation! -You: /opsx:apply +You: /openspec:apply AI: Working through tasks... ✓ Added logout button to header @@ -88,7 +88,7 @@ AI: Working through tasks... ✓ Added confirmation dialog All tasks complete! -You: /opsx:archive +You: /openspec:archive AI: ✓ Merged specs ✓ Archived change @@ -101,13 +101,13 @@ AI: ✓ Merged specs When requirements are unclear or you need to investigate first: ```text -/opsx:explore ──► /opsx:new ──► /opsx:continue ──► ... ──► /opsx:apply +/openspec:explore ──► /openspec:new ──► /openspec:continue ──► ... ──► /openspec:apply ``` **Example conversation:** ```text -You: /opsx:explore +You: /openspec:explore AI: What would you like to explore? @@ -127,11 +127,11 @@ AI: Let me help investigate... You: Let's tackle the data fetching issue. -You: /opsx:new optimize-product-list-fetching +You: /openspec:new optimize-product-list-fetching AI: Created change. Ready to create: proposal -You: /opsx:continue +You: /openspec:continue AI: Creating proposal based on our exploration... [Writes proposal incorporating investigation insights] @@ -139,7 +139,7 @@ AI: Creating proposal based on our exploration... Ready to create: specs, design Which would you like next? -You: /opsx:continue +You: /openspec:continue AI: Creating specs... ``` @@ -151,11 +151,11 @@ AI: Creating specs... Work on multiple changes at once: ```text -Change A: /opsx:new ──► /opsx:ff ──► /opsx:apply (in progress) +Change A: /openspec:new ──► /openspec:ff ──► /openspec:apply (in progress) │ context switch │ -Change B: /opsx:new ──► /opsx:ff ──────► /opsx:apply +Change B: /openspec:new ──► /openspec:ff ──────► /openspec:apply ``` **Example conversation:** @@ -163,27 +163,27 @@ Change B: /opsx:new ──► /opsx:ff ──────► /opsx:apply ```text You: I'm working on add-dark-mode but got pulled into a bug fix -You: /opsx:new fix-login-redirect +You: /openspec:new fix-login-redirect AI: Created openspec/changes/fix-login-redirect/ -You: /opsx:ff +You: /openspec:ff AI: ✓ Created planning artifacts -You: /opsx:apply +You: /openspec:apply AI: Working on fix-login-redirect... [Implements the fix] All tasks complete! -You: /opsx:archive +You: /openspec:archive AI: ✓ Archived fix-login-redirect You: Let's get back to dark mode -You: /opsx:apply add-dark-mode +You: /openspec:apply add-dark-mode AI: Resuming add-dark-mode... Picking up at task 2.3: Update Header... @@ -191,10 +191,10 @@ AI: Resuming add-dark-mode... **Best for:** Parallel work streams, urgent interrupts, team collaboration. -When you have multiple completed changes, use `/opsx:bulk-archive`: +When you have multiple completed changes, use `/openspec:bulk-archive`: ```text -You: /opsx:bulk-archive +You: /openspec:bulk-archive AI: Found 3 completed changes: - add-dark-mode (all tasks done) @@ -224,7 +224,7 @@ Bulk archive detects when multiple changes touch the same specs and resolves con The recommended completion flow: ```text -/opsx:apply ──► /opsx:verify ──► /opsx:archive +/openspec:apply ──► /openspec:verify ──► /openspec:archive │ │ validates prompts to sync implementation if needed @@ -232,10 +232,10 @@ The recommended completion flow: #### Verify: Check Your Work -`/opsx:verify` validates implementation against your artifacts across three dimensions: +`/openspec:verify` validates implementation against your artifacts across three dimensions: ```text -You: /opsx:verify +You: /openspec:verify AI: Verifying add-auth... @@ -277,10 +277,10 @@ Verify won't block archive, but it surfaces issues you might want to address fir #### Archive: Finalize the Change -`/opsx:archive` completes the change and moves it to the archive: +`/openspec:archive` completes the change and moves it to the archive: ```text -You: /opsx:archive +You: /openspec:archive AI: Archiving add-auth... @@ -305,17 +305,17 @@ Archive will prompt if specs aren't synced. It won't block on incomplete tasks, ## When to Use What -### `/opsx:ff` vs `/opsx:continue` +### `/openspec:ff` vs `/openspec:continue` | Situation | Use | |-----------|-----| -| Clear requirements, ready to build | `/opsx:ff` | -| Exploring, want to review each step | `/opsx:continue` | -| Want to iterate on proposal before specs | `/opsx:continue` | -| Time pressure, need to move fast | `/opsx:ff` | -| Complex change, want control | `/opsx:continue` | +| Clear requirements, ready to build | `/openspec:ff` | +| Exploring, want to review each step | `/openspec:continue` | +| Want to iterate on proposal before specs | `/openspec:continue` | +| Time pressure, need to move fast | `/openspec:ff` | +| Complex change, want control | `/openspec:continue` | -**Rule of thumb:** If you can describe the full scope upfront, use `/opsx:ff`. If you're figuring it out as you go, use `/opsx:continue`. +**Rule of thumb:** If you can describe the full scope upfront, use `/openspec:ff`. If you're figuring it out as you go, use `/openspec:continue`. ### When to Update vs Start Fresh @@ -373,12 +373,12 @@ One logical unit of work per change. If you're doing "add feature X and also ref - Can ship independently - Simpler rollback if needed -### Use `/opsx:explore` for Unclear Requirements +### Use `/openspec:explore` for Unclear Requirements Before committing to a change, explore the problem space: ```text -You: /opsx:explore +You: /openspec:explore AI: What would you like to explore? @@ -399,10 +399,10 @@ Exploration clarifies thinking before you create artifacts. ### Verify Before Archiving -Use `/opsx:verify` to check implementation matches artifacts: +Use `/openspec:verify` to check implementation matches artifacts: ```text -You: /opsx:verify +You: /openspec:verify AI: Verifying add-dark-mode... @@ -433,16 +433,16 @@ For full command details and options, see [Commands](commands.md). | Command | Purpose | When to Use | |---------|---------|-------------| -| `/opsx:propose` | Create change + planning artifacts | Fast default path (`core` profile) | -| `/opsx:explore` | Think through ideas | Unclear requirements, investigation | -| `/opsx:new` | Start a change scaffold | Expanded mode, explicit artifact control | -| `/opsx:continue` | Create next artifact | Expanded mode, step-by-step artifact creation | -| `/opsx:ff` | Create all planning artifacts | Expanded mode, clear scope | -| `/opsx:apply` | Implement tasks | Ready to write code | -| `/opsx:verify` | Validate implementation | Expanded mode, before archiving | -| `/opsx:sync` | Merge delta specs | Expanded mode, optional | -| `/opsx:archive` | Complete the change | All work finished | -| `/opsx:bulk-archive` | Archive multiple changes | Expanded mode, parallel work | +| `/openspec:propose` | Create change + planning artifacts | Fast default path (`core` profile) | +| `/openspec:explore` | Think through ideas | Unclear requirements, investigation | +| `/openspec:new` | Start a change scaffold | Expanded mode, explicit artifact control | +| `/openspec:continue` | Create next artifact | Expanded mode, step-by-step artifact creation | +| `/openspec:ff` | Create all planning artifacts | Expanded mode, clear scope | +| `/openspec:apply` | Implement tasks | Ready to write code | +| `/openspec:verify` | Validate implementation | Expanded mode, before archiving | +| `/openspec:sync` | Merge delta specs | Expanded mode, optional | +| `/openspec:archive` | Complete the change | All work finished | +| `/openspec:bulk-archive` | Archive multiple changes | Expanded mode, parallel work | ## Next Steps diff --git a/package-lock.json b/package-lock.json index e1daf932f..9340fe4bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@fission-ai/openspec", - "version": "1.1.1", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@fission-ai/openspec", - "version": "1.1.1", + "version": "1.2.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/src/core/command-generation/adapters/amazon-q.ts b/src/core/command-generation/adapters/amazon-q.ts index 0131c0638..d4507e76e 100644 --- a/src/core/command-generation/adapters/amazon-q.ts +++ b/src/core/command-generation/adapters/amazon-q.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Amazon Q adapter for command generation. - * File path: .amazonq/prompts/opsx-.md + * File path: .amazonq/prompts/openspec-.md * Frontmatter: description */ export const amazonQAdapter: ToolCommandAdapter = { toolId: 'amazon-q', getFilePath(commandId: string): string { - return path.join('.amazonq', 'prompts', `opsx-${commandId}.md`); + return path.join('.amazonq', 'prompts', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/antigravity.ts b/src/core/command-generation/adapters/antigravity.ts index e7a5d4919..b54abc037 100644 --- a/src/core/command-generation/adapters/antigravity.ts +++ b/src/core/command-generation/adapters/antigravity.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Antigravity adapter for command generation. - * File path: .agent/workflows/opsx-.md + * File path: .agent/workflows/openspec-.md * Frontmatter: description */ export const antigravityAdapter: ToolCommandAdapter = { toolId: 'antigravity', getFilePath(commandId: string): string { - return path.join('.agent', 'workflows', `opsx-${commandId}.md`); + return path.join('.agent', 'workflows', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/auggie.ts b/src/core/command-generation/adapters/auggie.ts index 2a52104c0..4b98143e7 100644 --- a/src/core/command-generation/adapters/auggie.ts +++ b/src/core/command-generation/adapters/auggie.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Auggie adapter for command generation. - * File path: .augment/commands/opsx-.md + * File path: .augment/commands/openspec-.md * Frontmatter: description, argument-hint */ export const auggieAdapter: ToolCommandAdapter = { toolId: 'auggie', getFilePath(commandId: string): string { - return path.join('.augment', 'commands', `opsx-${commandId}.md`); + return path.join('.augment', 'commands', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/claude.ts b/src/core/command-generation/adapters/claude.ts index 532b3a47b..d328184fd 100644 --- a/src/core/command-generation/adapters/claude.ts +++ b/src/core/command-generation/adapters/claude.ts @@ -32,14 +32,14 @@ function formatTagsArray(tags: string[]): string { /** * Claude Code adapter for command generation. - * File path: .claude/commands/opsx/.md + * File path: .claude/commands/openspec/.md * Frontmatter: name, description, category, tags */ export const claudeAdapter: ToolCommandAdapter = { toolId: 'claude', getFilePath(commandId: string): string { - return path.join('.claude', 'commands', 'opsx', `${commandId}.md`); + return path.join('.claude', 'commands', 'openspec', `${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/cline.ts b/src/core/command-generation/adapters/cline.ts index abc643164..1aa9df582 100644 --- a/src/core/command-generation/adapters/cline.ts +++ b/src/core/command-generation/adapters/cline.ts @@ -10,14 +10,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Cline adapter for command generation. - * File path: .clinerules/workflows/opsx-.md + * File path: .clinerules/workflows/openspec-.md * Format: Markdown header with description */ export const clineAdapter: ToolCommandAdapter = { toolId: 'cline', getFilePath(commandId: string): string { - return path.join('.clinerules', 'workflows', `opsx-${commandId}.md`); + return path.join('.clinerules', 'workflows', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/codebuddy.ts b/src/core/command-generation/adapters/codebuddy.ts index 54b7eebdc..2ac3c184b 100644 --- a/src/core/command-generation/adapters/codebuddy.ts +++ b/src/core/command-generation/adapters/codebuddy.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * CodeBuddy adapter for command generation. - * File path: .codebuddy/commands/opsx/.md + * File path: .codebuddy/commands/openspec/.md * Frontmatter: name, description, argument-hint */ export const codebuddyAdapter: ToolCommandAdapter = { toolId: 'codebuddy', getFilePath(commandId: string): string { - return path.join('.codebuddy', 'commands', 'opsx', `${commandId}.md`); + return path.join('.codebuddy', 'commands', 'openspec', `${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/codex.ts b/src/core/command-generation/adapters/codex.ts index 64e73550b..ec588b990 100644 --- a/src/core/command-generation/adapters/codex.ts +++ b/src/core/command-generation/adapters/codex.ts @@ -22,14 +22,14 @@ function getCodexHome(): string { /** * Codex adapter for command generation. - * File path: /prompts/opsx-.md (absolute, global) + * File path: /prompts/openspec-.md (absolute, global) * Frontmatter: description, argument-hint */ export const codexAdapter: ToolCommandAdapter = { toolId: 'codex', getFilePath(commandId: string): string { - return path.join(getCodexHome(), 'prompts', `opsx-${commandId}.md`); + return path.join(getCodexHome(), 'prompts', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/continue.ts b/src/core/command-generation/adapters/continue.ts index f6aac08b0..bab1e3bed 100644 --- a/src/core/command-generation/adapters/continue.ts +++ b/src/core/command-generation/adapters/continue.ts @@ -9,19 +9,19 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Continue adapter for command generation. - * File path: .continue/prompts/opsx-.prompt + * File path: .continue/prompts/openspec-.prompt * Frontmatter: name, description, invokable */ export const continueAdapter: ToolCommandAdapter = { toolId: 'continue', getFilePath(commandId: string): string { - return path.join('.continue', 'prompts', `opsx-${commandId}.prompt`); + return path.join('.continue', 'prompts', `openspec-${commandId}.prompt`); }, formatFile(content: CommandContent): string { return `--- -name: opsx-${content.id} +name: openspec-${content.id} description: ${content.description} invokable: true --- diff --git a/src/core/command-generation/adapters/costrict.ts b/src/core/command-generation/adapters/costrict.ts index 17628a124..f5afad716 100644 --- a/src/core/command-generation/adapters/costrict.ts +++ b/src/core/command-generation/adapters/costrict.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * CoStrict adapter for command generation. - * File path: .cospec/openspec/commands/opsx-.md + * File path: .cospec/openspec/commands/openspec-.md * Frontmatter: description, argument-hint */ export const costrictAdapter: ToolCommandAdapter = { toolId: 'costrict', getFilePath(commandId: string): string { - return path.join('.cospec', 'openspec', 'commands', `opsx-${commandId}.md`); + return path.join('.cospec', 'openspec', 'commands', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/crush.ts b/src/core/command-generation/adapters/crush.ts index b4d1a0b9d..4ff77f0ec 100644 --- a/src/core/command-generation/adapters/crush.ts +++ b/src/core/command-generation/adapters/crush.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Crush adapter for command generation. - * File path: .crush/commands/opsx/.md + * File path: .crush/commands/openspec/.md * Frontmatter: name, description, category, tags */ export const crushAdapter: ToolCommandAdapter = { toolId: 'crush', getFilePath(commandId: string): string { - return path.join('.crush', 'commands', 'opsx', `${commandId}.md`); + return path.join('.crush', 'commands', 'openspec', `${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/cursor.ts b/src/core/command-generation/adapters/cursor.ts index 85adedb03..1773faad4 100644 --- a/src/core/command-generation/adapters/cursor.ts +++ b/src/core/command-generation/adapters/cursor.ts @@ -25,20 +25,20 @@ function escapeYamlValue(value: string): string { /** * Cursor adapter for command generation. - * File path: .cursor/commands/opsx-.md - * Frontmatter: name (as /opsx-), id, category, description + * File path: .cursor/commands/openspec-.md + * Frontmatter: name (as /openspec-), id, category, description */ export const cursorAdapter: ToolCommandAdapter = { toolId: 'cursor', getFilePath(commandId: string): string { - return path.join('.cursor', 'commands', `opsx-${commandId}.md`); + return path.join('.cursor', 'commands', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { return `--- -name: /opsx-${content.id} -id: opsx-${content.id} +name: /openspec-${content.id} +id: openspec-${content.id} category: ${escapeYamlValue(content.category)} description: ${escapeYamlValue(content.description)} --- diff --git a/src/core/command-generation/adapters/factory.ts b/src/core/command-generation/adapters/factory.ts index 5031d5dc7..48130abc7 100644 --- a/src/core/command-generation/adapters/factory.ts +++ b/src/core/command-generation/adapters/factory.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Factory adapter for command generation. - * File path: .factory/commands/opsx-.md + * File path: .factory/commands/openspec-.md * Frontmatter: description, argument-hint */ export const factoryAdapter: ToolCommandAdapter = { toolId: 'factory', getFilePath(commandId: string): string { - return path.join('.factory', 'commands', `opsx-${commandId}.md`); + return path.join('.factory', 'commands', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/gemini.ts b/src/core/command-generation/adapters/gemini.ts index 2c08656f4..1f2b066fb 100644 --- a/src/core/command-generation/adapters/gemini.ts +++ b/src/core/command-generation/adapters/gemini.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Gemini adapter for command generation. - * File path: .gemini/commands/opsx/.toml + * File path: .gemini/commands/openspec/.toml * Format: TOML with description and prompt fields */ export const geminiAdapter: ToolCommandAdapter = { toolId: 'gemini', getFilePath(commandId: string): string { - return path.join('.gemini', 'commands', 'opsx', `${commandId}.toml`); + return path.join('.gemini', 'commands', 'openspec', `${commandId}.toml`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/github-copilot.ts b/src/core/command-generation/adapters/github-copilot.ts index 4eac7f1b6..c0202d0db 100644 --- a/src/core/command-generation/adapters/github-copilot.ts +++ b/src/core/command-generation/adapters/github-copilot.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * GitHub Copilot adapter for command generation. - * File path: .github/prompts/opsx-.prompt.md + * File path: .github/prompts/openspec-.prompt.md * Frontmatter: description */ export const githubCopilotAdapter: ToolCommandAdapter = { toolId: 'github-copilot', getFilePath(commandId: string): string { - return path.join('.github', 'prompts', `opsx-${commandId}.prompt.md`); + return path.join('.github', 'prompts', `openspec-${commandId}.prompt.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/iflow.ts b/src/core/command-generation/adapters/iflow.ts index d60a3f0b1..db98d36a9 100644 --- a/src/core/command-generation/adapters/iflow.ts +++ b/src/core/command-generation/adapters/iflow.ts @@ -9,20 +9,20 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * iFlow adapter for command generation. - * File path: .iflow/commands/opsx-.md + * File path: .iflow/commands/openspec-.md * Frontmatter: name, id, category, description */ export const iflowAdapter: ToolCommandAdapter = { toolId: 'iflow', getFilePath(commandId: string): string { - return path.join('.iflow', 'commands', `opsx-${commandId}.md`); + return path.join('.iflow', 'commands', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { return `--- -name: /opsx-${content.id} -id: opsx-${content.id} +name: /openspec-${content.id} +id: openspec-${content.id} category: ${content.category} description: ${content.description} --- diff --git a/src/core/command-generation/adapters/kilocode.ts b/src/core/command-generation/adapters/kilocode.ts index bb60c4dd7..0faea8eca 100644 --- a/src/core/command-generation/adapters/kilocode.ts +++ b/src/core/command-generation/adapters/kilocode.ts @@ -10,14 +10,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Kilo Code adapter for command generation. - * File path: .kilocode/workflows/opsx-.md + * File path: .kilocode/workflows/openspec-.md * Format: Plain markdown without frontmatter */ export const kilocodeAdapter: ToolCommandAdapter = { toolId: 'kilocode', getFilePath(commandId: string): string { - return path.join('.kilocode', 'workflows', `opsx-${commandId}.md`); + return path.join('.kilocode', 'workflows', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/kiro.ts b/src/core/command-generation/adapters/kiro.ts index 2e8a4ca4c..6a6a37cc5 100644 --- a/src/core/command-generation/adapters/kiro.ts +++ b/src/core/command-generation/adapters/kiro.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Kiro adapter for command generation. - * File path: .kiro/prompts/opsx-.prompt.md + * File path: .kiro/prompts/openspec-.prompt.md * Frontmatter: description */ export const kiroAdapter: ToolCommandAdapter = { toolId: 'kiro', getFilePath(commandId: string): string { - return path.join('.kiro', 'prompts', `opsx-${commandId}.prompt.md`); + return path.join('.kiro', 'prompts', `openspec-${commandId}.prompt.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/opencode.ts b/src/core/command-generation/adapters/opencode.ts index 301664b47..6af24e0dc 100644 --- a/src/core/command-generation/adapters/opencode.ts +++ b/src/core/command-generation/adapters/opencode.ts @@ -10,14 +10,14 @@ import { transformToHyphenCommands } from '../../../utils/command-references.js' /** * OpenCode adapter for command generation. - * File path: .opencode/commands/opsx-.md + * File path: .opencode/commands/openspec-.md * Frontmatter: description */ export const opencodeAdapter: ToolCommandAdapter = { toolId: 'opencode', getFilePath(commandId: string): string { - return path.join('.opencode', 'commands', `opsx-${commandId}.md`); + return path.join('.opencode', 'commands', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/pi.ts b/src/core/command-generation/adapters/pi.ts index cb8d2b331..e873f1ff5 100644 --- a/src/core/command-generation/adapters/pi.ts +++ b/src/core/command-generation/adapters/pi.ts @@ -25,14 +25,14 @@ function escapeYamlValue(value: string): string { /** * Pi adapter for prompt template generation. - * File path: .pi/prompts/opsx-.md + * File path: .pi/prompts/openspec-.md * Frontmatter: description */ export const piAdapter: ToolCommandAdapter = { toolId: 'pi', getFilePath(commandId: string): string { - return path.join('.pi', 'prompts', `opsx-${commandId}.md`); + return path.join('.pi', 'prompts', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/qoder.ts b/src/core/command-generation/adapters/qoder.ts index 608fc9ae2..8438068f0 100644 --- a/src/core/command-generation/adapters/qoder.ts +++ b/src/core/command-generation/adapters/qoder.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Qoder adapter for command generation. - * File path: .qoder/commands/opsx/.md + * File path: .qoder/commands/openspec/.md * Frontmatter: name, description, category, tags */ export const qoderAdapter: ToolCommandAdapter = { toolId: 'qoder', getFilePath(commandId: string): string { - return path.join('.qoder', 'commands', 'opsx', `${commandId}.md`); + return path.join('.qoder', 'commands', 'openspec', `${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/qwen.ts b/src/core/command-generation/adapters/qwen.ts index 0ee640b3c..675d3e6ac 100644 --- a/src/core/command-generation/adapters/qwen.ts +++ b/src/core/command-generation/adapters/qwen.ts @@ -9,14 +9,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * Qwen adapter for command generation. - * File path: .qwen/commands/opsx-.toml + * File path: .qwen/commands/openspec-.toml * Format: TOML with description and prompt fields */ export const qwenAdapter: ToolCommandAdapter = { toolId: 'qwen', getFilePath(commandId: string): string { - return path.join('.qwen', 'commands', `opsx-${commandId}.toml`); + return path.join('.qwen', 'commands', `openspec-${commandId}.toml`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/roocode.ts b/src/core/command-generation/adapters/roocode.ts index 529298578..b20a62f91 100644 --- a/src/core/command-generation/adapters/roocode.ts +++ b/src/core/command-generation/adapters/roocode.ts @@ -10,14 +10,14 @@ import type { CommandContent, ToolCommandAdapter } from '../types.js'; /** * RooCode adapter for command generation. - * File path: .roo/commands/opsx-.md + * File path: .roo/commands/openspec-.md * Format: Markdown header with description */ export const roocodeAdapter: ToolCommandAdapter = { toolId: 'roocode', getFilePath(commandId: string): string { - return path.join('.roo', 'commands', `opsx-${commandId}.md`); + return path.join('.roo', 'commands', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/adapters/windsurf.ts b/src/core/command-generation/adapters/windsurf.ts index 59c86d8e0..e20c3d1b7 100644 --- a/src/core/command-generation/adapters/windsurf.ts +++ b/src/core/command-generation/adapters/windsurf.ts @@ -33,14 +33,14 @@ function formatTagsArray(tags: string[]): string { /** * Windsurf adapter for command generation. - * File path: .windsurf/workflows/opsx-.md + * File path: .windsurf/workflows/openspec-.md * Frontmatter: name, description, category, tags */ export const windsurfAdapter: ToolCommandAdapter = { toolId: 'windsurf', getFilePath(commandId: string): string { - return path.join('.windsurf', 'workflows', `opsx-${commandId}.md`); + return path.join('.windsurf', 'workflows', `openspec-${commandId}.md`); }, formatFile(content: CommandContent): string { diff --git a/src/core/command-generation/types.ts b/src/core/command-generation/types.ts index 582d8c784..37e8fc220 100644 --- a/src/core/command-generation/types.ts +++ b/src/core/command-generation/types.ts @@ -35,7 +35,7 @@ export interface ToolCommandAdapter { /** * Returns the file path for a command. * @param commandId - The command identifier (e.g., 'explore') - * @returns Path from project root (e.g., '.claude/commands/opsx/explore.md'). + * @returns Path from project root (e.g., '.claude/commands/openspec/explore.md'). * May be absolute for tools with global-scoped prompts (e.g., Codex). */ getFilePath(commandId: string): string; diff --git a/src/core/init.ts b/src/core/init.ts index 95728dc7e..d1f5b713f 100644 --- a/src/core/init.ts +++ b/src/core/init.ts @@ -1,7 +1,7 @@ /** * Init Command * - * Sets up OpenSpec with Agent Skills and /opsx:* slash commands. + * Sets up OpenSpec with Agent Skills and /openspec:* slash commands. * This is the unified setup command that replaces both the old init and experimental commands. */ @@ -703,10 +703,10 @@ export class InitCommand { console.log(); if (activeWorkflows.includes('propose')) { console.log(chalk.bold('Getting started:')); - console.log(' Start your first change: /opsx:propose "your idea"'); + console.log(' Start your first change: /openspec:propose "your idea"'); } else if (activeWorkflows.includes('new')) { console.log(chalk.bold('Getting started:')); - console.log(' Start your first change: /opsx:new "your idea"'); + console.log(' Start your first change: /openspec:new "your idea"'); } else { console.log("Done. Run 'openspec config profile' to configure your workflows."); } diff --git a/src/core/legacy-cleanup.ts b/src/core/legacy-cleanup.ts index b165b9e99..bf0ccaf73 100644 --- a/src/core/legacy-cleanup.ts +++ b/src/core/legacy-cleanup.ts @@ -30,31 +30,31 @@ export const LEGACY_CONFIG_FILES = [ * Some tools used a directory structure, others used individual files. */ export const LEGACY_SLASH_COMMAND_PATHS: Record = { - // Directory-based: .tooldir/commands/openspec/ or .tooldir/commands/openspec/*.md - 'claude': { type: 'directory', path: '.claude/commands/openspec' }, - 'codebuddy': { type: 'directory', path: '.codebuddy/commands/openspec' }, - 'qoder': { type: 'directory', path: '.qoder/commands/openspec' }, - 'crush': { type: 'directory', path: '.crush/commands/openspec' }, - 'gemini': { type: 'directory', path: '.gemini/commands/openspec' }, + // Directory-based: .tooldir/commands/opsx/ or .tooldir/commands/opsx/*.md + 'claude': { type: 'directory', path: '.claude/commands/opsx' }, + 'codebuddy': { type: 'directory', path: '.codebuddy/commands/opsx' }, + 'qoder': { type: 'directory', path: '.qoder/commands/opsx' }, + 'crush': { type: 'directory', path: '.crush/commands/opsx' }, + 'gemini': { type: 'directory', path: '.gemini/commands/opsx' }, 'costrict': { type: 'directory', path: '.cospec/openspec/commands' }, - // File-based: individual openspec-*.md files in a commands/workflows/prompts folder - 'cursor': { type: 'files', pattern: '.cursor/commands/openspec-*.md' }, - 'windsurf': { type: 'files', pattern: '.windsurf/workflows/openspec-*.md' }, - 'kilocode': { type: 'files', pattern: '.kilocode/workflows/openspec-*.md' }, - 'kiro': { type: 'files', pattern: '.kiro/prompts/openspec-*.prompt.md' }, - 'github-copilot': { type: 'files', pattern: '.github/prompts/openspec-*.prompt.md' }, - 'amazon-q': { type: 'files', pattern: '.amazonq/prompts/openspec-*.md' }, - 'cline': { type: 'files', pattern: '.clinerules/workflows/openspec-*.md' }, - 'roocode': { type: 'files', pattern: '.roo/commands/openspec-*.md' }, - 'auggie': { type: 'files', pattern: '.augment/commands/openspec-*.md' }, - 'factory': { type: 'files', pattern: '.factory/commands/openspec-*.md' }, + // File-based: individual opsx-*.md files in a commands/workflows/prompts folder + 'cursor': { type: 'files', pattern: '.cursor/commands/opsx-*.md' }, + 'windsurf': { type: 'files', pattern: '.windsurf/workflows/opsx-*.md' }, + 'kilocode': { type: 'files', pattern: '.kilocode/workflows/opsx-*.md' }, + 'kiro': { type: 'files', pattern: '.kiro/prompts/opsx-*.prompt.md' }, + 'github-copilot': { type: 'files', pattern: '.github/prompts/opsx-*.prompt.md' }, + 'amazon-q': { type: 'files', pattern: '.amazonq/prompts/opsx-*.md' }, + 'cline': { type: 'files', pattern: '.clinerules/workflows/opsx-*.md' }, + 'roocode': { type: 'files', pattern: '.roo/commands/opsx-*.md' }, + 'auggie': { type: 'files', pattern: '.augment/commands/opsx-*.md' }, + 'factory': { type: 'files', pattern: '.factory/commands/opsx-*.md' }, 'opencode': { type: 'files', pattern: ['.opencode/command/opsx-*.md', '.opencode/command/openspec-*.md'] }, - 'continue': { type: 'files', pattern: '.continue/prompts/openspec-*.prompt' }, - 'antigravity': { type: 'files', pattern: '.agent/workflows/openspec-*.md' }, - 'iflow': { type: 'files', pattern: '.iflow/commands/openspec-*.md' }, - 'qwen': { type: 'files', pattern: '.qwen/commands/openspec-*.toml' }, - 'codex': { type: 'files', pattern: '.codex/prompts/openspec-*.md' }, + 'continue': { type: 'files', pattern: '.continue/prompts/opsx-*.prompt' }, + 'antigravity': { type: 'files', pattern: '.agent/workflows/opsx-*.md' }, + 'iflow': { type: 'files', pattern: '.iflow/commands/opsx-*.md' }, + 'qwen': { type: 'files', pattern: '.qwen/commands/opsx-*.toml' }, + 'codex': { type: 'files', pattern: '.codex/prompts/opsx-*.md' }, }; /** @@ -445,7 +445,7 @@ export function formatCleanupSummary(result: CleanupResult): string { } for (const dir of result.deletedDirs) { - lines.push(` ✓ Removed ${dir}/ (replaced by /opsx:*)`); + lines.push(` ✓ Removed ${dir}/ (replaced by /openspec:*)`); } for (const file of result.modifiedFiles) { diff --git a/src/core/migration.ts b/src/core/migration.ts index 48aaa41ee..049ce5b27 100644 --- a/src/core/migration.ts +++ b/src/core/migration.ts @@ -127,5 +127,5 @@ export function migrateIfNeeded(projectPath: string, tools: AIToolOption[]): voi saveGlobalConfig(config); console.log(`Migrated: custom profile with ${installedWorkflows.length} workflows`); - console.log("New in this version: /opsx:propose. Try 'openspec config profile core' for the streamlined experience."); + console.log("New in this version: /openspec:propose. Try 'openspec config profile core' for the streamlined experience."); } diff --git a/src/core/templates/workflows/apply-change.ts b/src/core/templates/workflows/apply-change.ts index c5044698a..5cff50d01 100644 --- a/src/core/templates/workflows/apply-change.ts +++ b/src/core/templates/workflows/apply-change.ts @@ -23,7 +23,7 @@ export function getApplyChangeSkillTemplate(): SkillTemplate { - Auto-select if only one active change exists - If ambiguous, run \`openspec list --json\` to get available changes and use the **AskUserQuestion tool** to let the user select - Always announce: "Using change: " and how to override (e.g., \`/opsx:apply \`). + Always announce: "Using change: " and how to override (e.g., \`/openspec:apply \`). 2. **Check status to understand the schema** \`\`\`bash @@ -163,13 +163,13 @@ This skill supports the "actions on a change" model: export function getOpsxApplyCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Apply', + name: 'OpenSpec: Apply', description: 'Implement tasks from an OpenSpec change (Experimental)', category: 'Workflow', tags: ['workflow', 'artifacts', 'experimental'], content: `Implement tasks from an OpenSpec change. -**Input**: Optionally specify a change name (e.g., \`/opsx:apply add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. +**Input**: Optionally specify a change name (e.g., \`/openspec:apply add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. **Steps** @@ -180,7 +180,7 @@ export function getOpsxApplyCommandTemplate(): CommandTemplate { - Auto-select if only one active change exists - If ambiguous, run \`openspec list --json\` to get available changes and use the **AskUserQuestion tool** to let the user select - Always announce: "Using change: " and how to override (e.g., \`/opsx:apply \`). + Always announce: "Using change: " and how to override (e.g., \`/openspec:apply \`). 2. **Check status to understand the schema** \`\`\`bash @@ -203,7 +203,7 @@ export function getOpsxApplyCommandTemplate(): CommandTemplate { - Dynamic instruction based on current state **Handle states:** - - If \`state: "blocked"\` (missing artifacts): show message, suggest using \`/opsx:continue\` + - If \`state: "blocked"\` (missing artifacts): show message, suggest using \`/openspec:continue\` - If \`state: "all_done"\`: congratulate, suggest archive - Otherwise: proceed to implementation @@ -273,7 +273,7 @@ Working on task 4/7: - [x] Task 2 ... -All tasks complete! You can archive this change with \`/opsx:archive\`. +All tasks complete! You can archive this change with \`/openspec:archive\`. \`\`\` **Output On Pause (Issue Encountered)** diff --git a/src/core/templates/workflows/archive-change.ts b/src/core/templates/workflows/archive-change.ts index 1c37ffde0..908d29aad 100644 --- a/src/core/templates/workflows/archive-change.ts +++ b/src/core/templates/workflows/archive-change.ts @@ -121,13 +121,13 @@ All artifacts complete. All tasks complete. export function getOpsxArchiveCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Archive', + name: 'OpenSpec: Archive', description: 'Archive a completed change in the experimental workflow', category: 'Workflow', tags: ['workflow', 'archive', 'experimental'], content: `Archive a completed change in the experimental workflow. -**Input**: Optionally specify a change name after \`/opsx:archive\` (e.g., \`/opsx:archive add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. +**Input**: Optionally specify a change name after \`/openspec:archive\` (e.g., \`/openspec:archive add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. **Steps** diff --git a/src/core/templates/workflows/bulk-archive-change.ts b/src/core/templates/workflows/bulk-archive-change.ts index d57db8aa0..6124d91e4 100644 --- a/src/core/templates/workflows/bulk-archive-change.ts +++ b/src/core/templates/workflows/bulk-archive-change.ts @@ -253,7 +253,7 @@ No active changes found. Create a new change to get started. export function getOpsxBulkArchiveCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Bulk Archive', + name: 'OpenSpec: Bulk Archive', description: 'Archive multiple completed changes at once', category: 'Workflow', tags: ['workflow', 'archive', 'experimental', 'bulk'], diff --git a/src/core/templates/workflows/continue-change.ts b/src/core/templates/workflows/continue-change.ts index 4b2176728..c2940f6a2 100644 --- a/src/core/templates/workflows/continue-change.ts +++ b/src/core/templates/workflows/continue-change.ts @@ -125,13 +125,13 @@ For other schemas, follow the \`instruction\` field from the CLI output. export function getOpsxContinueCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Continue', + name: 'OpenSpec: Continue', description: 'Continue working on a change - create the next artifact (Experimental)', category: 'Workflow', tags: ['workflow', 'artifacts', 'experimental'], content: `Continue working on a change by creating the next artifact. -**Input**: Optionally specify a change name after \`/opsx:continue\` (e.g., \`/opsx:continue add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. +**Input**: Optionally specify a change name after \`/openspec:continue\` (e.g., \`/openspec:continue add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. **Steps** @@ -165,7 +165,7 @@ export function getOpsxContinueCommandTemplate(): CommandTemplate { **If all artifacts are complete (\`isComplete: true\`)**: - Congratulate the user - Show final status including the schema used - - Suggest: "All artifacts created! You can now implement this change with \`/opsx:apply\` or archive it with \`/opsx:archive\`." + - Suggest: "All artifacts created! You can now implement this change with \`/openspec:apply\` or archive it with \`/openspec:archive\`." - STOP --- @@ -209,7 +209,7 @@ After each invocation, show: - Schema workflow being used - Current progress (N/M complete) - What artifacts are now unlocked -- Prompt: "Run \`/opsx:continue\` to create the next artifact" +- Prompt: "Run \`/openspec:continue\` to create the next artifact" **Artifact Creation Guidelines** diff --git a/src/core/templates/workflows/explore.ts b/src/core/templates/workflows/explore.ts index 059d2ecae..d722f2f24 100644 --- a/src/core/templates/workflows/explore.ts +++ b/src/core/templates/workflows/explore.ts @@ -202,7 +202,7 @@ You: [reads codebase] **User is stuck mid-implementation:** \`\`\` -User: /opsx:explore add-auth-system +User: /openspec:explore add-auth-system The OAuth integration is more complex than expected You: [reads change artifacts] @@ -295,7 +295,7 @@ But this summary is optional. Sometimes the thinking IS the value. export function getOpsxExploreCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Explore', + name: 'OpenSpec: Explore', description: 'Enter explore mode - think through ideas, investigate problems, clarify requirements', category: 'Workflow', tags: ['workflow', 'explore', 'experimental', 'thinking'], @@ -305,7 +305,7 @@ export function getOpsxExploreCommandTemplate(): CommandTemplate { **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. -**Input**: The argument after \`/opsx:explore\` is whatever the user wants to think about. Could be: +**Input**: The argument after \`/openspec:explore\` is whatever the user wants to think about. Could be: - A vague idea: "real-time collaboration" - A specific problem: "the auth system is getting unwieldy" - A change name: "add-dark-mode" (to explore in context of that change) diff --git a/src/core/templates/workflows/ff-change.ts b/src/core/templates/workflows/ff-change.ts index 9e02983be..a8c7e7562 100644 --- a/src/core/templates/workflows/ff-change.ts +++ b/src/core/templates/workflows/ff-change.ts @@ -82,7 +82,7 @@ After completing all artifacts, summarize: - Change name and location - List of artifacts created with brief descriptions - What's ready: "All artifacts created! Ready for implementation." -- Prompt: "Run \`/opsx:apply\` or ask me to implement to start working on the tasks." +- Prompt: "Run \`/openspec:apply\` or ask me to implement to start working on the tasks." **Artifact Creation Guidelines** @@ -108,13 +108,13 @@ After completing all artifacts, summarize: export function getOpsxFfCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Fast Forward', + name: 'OpenSpec: Fast Forward', description: 'Create a change and generate all artifacts needed for implementation in one go', category: 'Workflow', tags: ['workflow', 'artifacts', 'experimental'], content: `Fast-forward through artifact creation - generate everything needed to start implementation. -**Input**: The argument after \`/opsx:ff\` is the change name (kebab-case), OR a description of what the user wants to build. +**Input**: The argument after \`/openspec:ff\` is the change name (kebab-case), OR a description of what the user wants to build. **Steps** @@ -184,7 +184,7 @@ After completing all artifacts, summarize: - Change name and location - List of artifacts created with brief descriptions - What's ready: "All artifacts created! Ready for implementation." -- Prompt: "Run \`/opsx:apply\` to start implementing." +- Prompt: "Run \`/openspec:apply\` to start implementing." **Artifact Creation Guidelines** diff --git a/src/core/templates/workflows/new-change.ts b/src/core/templates/workflows/new-change.ts index 10017422f..75e882359 100644 --- a/src/core/templates/workflows/new-change.ts +++ b/src/core/templates/workflows/new-change.ts @@ -81,13 +81,13 @@ After completing the steps, summarize: export function getOpsxNewCommandTemplate(): CommandTemplate { return { - name: 'OPSX: New', + name: 'OpenSpec: New', description: 'Start a new change using the experimental artifact workflow (OPSX)', category: 'Workflow', tags: ['workflow', 'artifacts', 'experimental'], content: `Start a new change using the experimental artifact-driven approach. -**Input**: The argument after \`/opsx:new\` is the change name (kebab-case), OR a description of what the user wants to build. +**Input**: The argument after \`/openspec:new\` is the change name (kebab-case), OR a description of what the user wants to build. **Steps** @@ -139,13 +139,13 @@ After completing the steps, summarize: - Schema/workflow being used and its artifact sequence - Current status (0/N artifacts complete) - The template for the first artifact -- Prompt: "Ready to create the first artifact? Run \`/opsx:continue\` or just describe what this change is about and I'll draft it." +- Prompt: "Ready to create the first artifact? Run \`/openspec:continue\` or just describe what this change is about and I'll draft it." **Guardrails** - Do NOT create any artifacts yet - just show the instructions - Do NOT advance beyond showing the first artifact template - If the name is invalid (not kebab-case), ask for a valid name -- If a change with that name already exists, suggest using \`/opsx:continue\` instead +- If a change with that name already exists, suggest using \`/openspec:continue\` instead - Pass --schema if using a non-default workflow` }; } diff --git a/src/core/templates/workflows/onboard.ts b/src/core/templates/workflows/onboard.ts index f9280fa07..b3ae0f21f 100644 --- a/src/core/templates/workflows/onboard.ts +++ b/src/core/templates/workflows/onboard.ts @@ -34,7 +34,7 @@ openspec --version 2>&1 || echo "CLI_NOT_INSTALLED" \`\`\` **If CLI not installed:** -> OpenSpec CLI is not installed. Install it first, then come back to \`/opsx:onboard\`. +> OpenSpec CLI is not installed. Install it first, then come back to \`/openspec:onboard\`. Stop here if not installed. @@ -161,7 +161,7 @@ Spend 1-2 minutes investigating the relevant code: │ [Optional: ASCII diagram if helpful] │ └─────────────────────────────────────────┘ -Explore mode (\`/opsx:explore\`) is for this kind of thinking—investigating before implementing. You can use it anytime you need to think through a problem. +Explore mode (\`/openspec:explore\`) is for this kind of thinking—investigating before implementing. You can use it anytime you need to think through a problem. Now let's create a change to hold our work. \`\`\` @@ -479,25 +479,25 @@ This same rhythm works for any size change—a small fix or a major feature. | Command | What it does | |---------|--------------| -| \`/opsx:propose\` | Create a change and generate all artifacts | -| \`/opsx:explore\` | Think through problems before/during work | -| \`/opsx:apply\` | Implement tasks from a change | -| \`/opsx:archive\` | Archive a completed change | +| \`/openspec:propose\` | Create a change and generate all artifacts | +| \`/openspec:explore\` | Think through problems before/during work | +| \`/openspec:apply\` | Implement tasks from a change | +| \`/openspec:archive\` | Archive a completed change | **Additional commands:** | Command | What it does | |---------|--------------| -| \`/opsx:new\` | Start a new change, step through artifacts one at a time | -| \`/opsx:continue\` | Continue working on an existing change | -| \`/opsx:ff\` | Fast-forward: create all artifacts at once | -| \`/opsx:verify\` | Verify implementation matches artifacts | +| \`/openspec:new\` | Start a new change, step through artifacts one at a time | +| \`/openspec:continue\` | Continue working on an existing change | +| \`/openspec:ff\` | Fast-forward: create all artifacts at once | +| \`/openspec:verify\` | Verify implementation matches artifacts | --- ## What's Next? -Try \`/opsx:propose\` on something you actually want to build. You've got the rhythm now! +Try \`/openspec:propose\` on something you actually want to build. You've got the rhythm now! \`\`\` --- @@ -512,8 +512,8 @@ If the user says they need to stop, want to pause, or seem disengaged: No problem! Your change is saved at \`openspec/changes//\`. To pick up where we left off later: -- \`/opsx:continue \` - Resume artifact creation -- \`/opsx:apply \` - Jump to implementation (if tasks exist) +- \`/openspec:continue \` - Resume artifact creation +- \`/openspec:apply \` - Jump to implementation (if tasks exist) The work won't be lost. Come back whenever you're ready. \`\`\` @@ -531,21 +531,21 @@ If the user says they just want to see the commands or skip the tutorial: | Command | What it does | |---------|--------------| -| \`/opsx:propose \` | Create a change and generate all artifacts | -| \`/opsx:explore\` | Think through problems (no code changes) | -| \`/opsx:apply \` | Implement tasks | -| \`/opsx:archive \` | Archive when done | +| \`/openspec:propose \` | Create a change and generate all artifacts | +| \`/openspec:explore\` | Think through problems (no code changes) | +| \`/openspec:apply \` | Implement tasks | +| \`/openspec:archive \` | Archive when done | **Additional commands:** | Command | What it does | |---------|--------------| -| \`/opsx:new \` | Start a new change, step by step | -| \`/opsx:continue \` | Continue an existing change | -| \`/opsx:ff \` | Fast-forward: all artifacts at once | -| \`/opsx:verify \` | Verify implementation | +| \`/openspec:new \` | Start a new change, step by step | +| \`/openspec:continue \` | Continue an existing change | +| \`/openspec:ff \` | Fast-forward: all artifacts at once | +| \`/openspec:verify \` | Verify implementation | -Try \`/opsx:propose\` to start your first change. +Try \`/openspec:propose\` to start your first change. \`\`\` Exit gracefully. @@ -565,7 +565,7 @@ Exit gracefully. export function getOpsxOnboardCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Onboard', + name: 'OpenSpec: Onboard', description: 'Guided onboarding - walk through a complete OpenSpec workflow cycle with narration', category: 'Workflow', tags: ['workflow', 'onboarding', 'tutorial', 'learning'], diff --git a/src/core/templates/workflows/propose.ts b/src/core/templates/workflows/propose.ts index 74a9ce2d0..cb2c23cf2 100644 --- a/src/core/templates/workflows/propose.ts +++ b/src/core/templates/workflows/propose.ts @@ -17,7 +17,7 @@ I'll create a change with artifacts: - design.md (how) - tasks.md (implementation steps) -When ready to implement, run /opsx:apply +When ready to implement, run /openspec:apply --- @@ -91,7 +91,7 @@ After completing all artifacts, summarize: - Change name and location - List of artifacts created with brief descriptions - What's ready: "All artifacts created! Ready for implementation." -- Prompt: "Run \`/opsx:apply\` or ask me to implement to start working on the tasks." +- Prompt: "Run \`/openspec:apply\` or ask me to implement to start working on the tasks." **Artifact Creation Guidelines** @@ -117,7 +117,7 @@ After completing all artifacts, summarize: export function getOpsxProposeCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Propose', + name: 'OpenSpec: Propose', description: 'Propose a new change - create it and generate all artifacts in one step', category: 'Workflow', tags: ['workflow', 'artifacts', 'experimental'], @@ -128,11 +128,11 @@ I'll create a change with artifacts: - design.md (how) - tasks.md (implementation steps) -When ready to implement, run /opsx:apply +When ready to implement, run /openspec:apply --- -**Input**: The argument after \`/opsx:propose\` is the change name (kebab-case), OR a description of what the user wants to build. +**Input**: The argument after \`/openspec:propose\` is the change name (kebab-case), OR a description of what the user wants to build. **Steps** @@ -202,7 +202,7 @@ After completing all artifacts, summarize: - Change name and location - List of artifacts created with brief descriptions - What's ready: "All artifacts created! Ready for implementation." -- Prompt: "Run \`/opsx:apply\` to start implementing." +- Prompt: "Run \`/openspec:apply\` to start implementing." **Artifact Creation Guidelines** diff --git a/src/core/templates/workflows/sync-specs.ts b/src/core/templates/workflows/sync-specs.ts index 34da4276e..a317727e9 100644 --- a/src/core/templates/workflows/sync-specs.ts +++ b/src/core/templates/workflows/sync-specs.ts @@ -145,7 +145,7 @@ Main specs are now updated. The change remains active - archive when implementat export function getOpsxSyncCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Sync', + name: 'OpenSpec: Sync', description: 'Sync delta specs from a change to main specs', category: 'Workflow', tags: ['workflow', 'specs', 'experimental'], @@ -153,7 +153,7 @@ export function getOpsxSyncCommandTemplate(): CommandTemplate { This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement). -**Input**: Optionally specify a change name after \`/opsx:sync\` (e.g., \`/opsx:sync add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. +**Input**: Optionally specify a change name after \`/openspec:sync\` (e.g., \`/openspec:sync add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. **Steps** diff --git a/src/core/templates/workflows/verify-change.ts b/src/core/templates/workflows/verify-change.ts index 825f7ecca..ae69ef40c 100644 --- a/src/core/templates/workflows/verify-change.ts +++ b/src/core/templates/workflows/verify-change.ts @@ -175,13 +175,13 @@ Use clear markdown with: export function getOpsxVerifyCommandTemplate(): CommandTemplate { return { - name: 'OPSX: Verify', + name: 'OpenSpec: Verify', description: 'Verify implementation matches change artifacts before archiving', category: 'Workflow', tags: ['workflow', 'verify', 'experimental'], content: `Verify that an implementation matches the change artifacts (specs, tasks, design). -**Input**: Optionally specify a change name after \`/opsx:verify\` (e.g., \`/opsx:verify add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. +**Input**: Optionally specify a change name after \`/openspec:verify\` (e.g., \`/openspec:verify add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. **Steps** diff --git a/src/core/update.ts b/src/core/update.ts index 62db8a08f..eae25c1c0 100644 --- a/src/core/update.ts +++ b/src/core/update.ts @@ -89,7 +89,7 @@ export class UpdateCommand { } // 2. Perform one-time migration if needed before any legacy upgrade generation. - // Use detected tool directories to preserve existing opsx skills/commands. + // Use detected tool directories to preserve existing openspec skills/commands. const detectedTools = getAvailableTools(resolvedProjectPath); migrateIfNeededShared(resolvedProjectPath, detectedTools); @@ -268,9 +268,9 @@ export class UpdateCommand { if (newlyConfiguredTools.length > 0) { console.log(); console.log(chalk.bold('Getting started:')); - console.log(' /opsx:new Start a new change'); - console.log(' /opsx:continue Create the next artifact'); - console.log(' /opsx:apply Implement tasks'); + console.log(' /openspec:new Start a new change'); + console.log(' /openspec:continue Create the next artifact'); + console.log(' /openspec:apply Implement tasks'); console.log(); console.log(`Learn more: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec')}`); } diff --git a/src/ui/welcome-screen.ts b/src/ui/welcome-screen.ts index 5ed26b6a1..0510a39d2 100644 --- a/src/ui/welcome-screen.ts +++ b/src/ui/welcome-screen.ts @@ -22,12 +22,12 @@ function getWelcomeText(): string[] { '', chalk.white('This setup will configure:'), chalk.dim(' • Agent Skills for AI tools'), - chalk.dim(' • /opsx:* slash commands'), + chalk.dim(' • /openspec:* slash commands'), '', chalk.white('Quick start after setup:'), - ` ${chalk.yellow('/opsx:new')} ${chalk.dim('Create a change')}`, - ` ${chalk.yellow('/opsx:continue')} ${chalk.dim('Next artifact')}`, - ` ${chalk.yellow('/opsx:apply')} ${chalk.dim('Implement tasks')}`, + ` ${chalk.yellow('/openspec:new')} ${chalk.dim('Create a change')}`, + ` ${chalk.yellow('/openspec:continue')} ${chalk.dim('Next artifact')}`, + ` ${chalk.yellow('/openspec:apply')} ${chalk.dim('Implement tasks')}`, '', chalk.cyan('Press Enter to select tools...'), ]; diff --git a/src/utils/command-references.ts b/src/utils/command-references.ts index bfa49b9ff..e7a2ff82b 100644 --- a/src/utils/command-references.ts +++ b/src/utils/command-references.ts @@ -6,15 +6,15 @@ /** * Transforms colon-based command references to hyphen-based format. - * Converts `/opsx:` patterns to `/opsx-` for tools that use hyphen syntax. + * Converts `/openspec:` patterns to `/openspec-` for tools that use hyphen syntax. * * @param text - The text containing command references * @returns Text with command references transformed to hyphen format * * @example - * transformToHyphenCommands('/opsx:new') // returns '/opsx-new' - * transformToHyphenCommands('Use /opsx:apply to implement') // returns 'Use /opsx-apply to implement' + * transformToHyphenCommands('/openspec:new') // returns '/openspec-new' + * transformToHyphenCommands('Use /openspec:apply to implement') // returns 'Use /openspec-apply to implement' */ export function transformToHyphenCommands(text: string): string { - return text.replace(/\/opsx:/g, '/opsx-'); + return text.replace(/\/openspec:/g, '/openspec-'); } diff --git a/test/commands/artifact-workflow.test.ts b/test/commands/artifact-workflow.test.ts index 17ed97740..d1232ac57 100644 --- a/test/commands/artifact-workflow.test.ts +++ b/test/commands/artifact-workflow.test.ts @@ -650,9 +650,9 @@ artifacts: expect(stat.isFile()).toBe(true); // Verify commands were created with Cursor format - const commandFile = path.join(tempDir, '.cursor', 'commands', 'opsx-explore.md'); + const commandFile = path.join(tempDir, '.cursor', 'commands', 'openspec-explore.md'); const content = await fs.readFile(commandFile, 'utf-8'); - expect(content).toContain('name: /opsx-explore'); + expect(content).toContain('name: /openspec-explore'); }); it('creates skills for Windsurf tool', async () => { diff --git a/test/commands/config-profile.test.ts b/test/commands/config-profile.test.ts index ef116693a..d141a3a72 100644 --- a/test/commands/config-profile.test.ts +++ b/test/commands/config-profile.test.ts @@ -105,7 +105,7 @@ describe('config profile interactive flow', () => { const coreCommands = ['propose', 'explore', 'apply', 'archive']; for (const commandId of coreCommands) { - const commandPath = path.join(projectDir, '.claude', 'commands', 'opsx', `${commandId}.md`); + const commandPath = path.join(projectDir, '.claude', 'commands', 'openspec', `${commandId}.md`); fs.mkdirSync(path.dirname(commandPath), { recursive: true }); fs.writeFileSync(commandPath, `# ${commandId}\n`, 'utf-8'); } @@ -116,7 +116,7 @@ describe('config profile interactive flow', () => { fs.mkdirSync(path.dirname(syncSkillPath), { recursive: true }); fs.writeFileSync(syncSkillPath, 'name: openspec-sync-specs\n', 'utf-8'); - const syncCommandPath = path.join(projectDir, '.claude', 'commands', 'opsx', 'sync.md'); + const syncCommandPath = path.join(projectDir, '.claude', 'commands', 'openspec', 'sync.md'); fs.mkdirSync(path.dirname(syncCommandPath), { recursive: true }); fs.writeFileSync(syncCommandPath, '# sync\n', 'utf-8'); } diff --git a/test/core/command-generation/adapters.test.ts b/test/core/command-generation/adapters.test.ts index dab19bf3d..71db0dc17 100644 --- a/test/core/command-generation/adapters.test.ts +++ b/test/core/command-generation/adapters.test.ts @@ -42,12 +42,12 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = claudeAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.claude', 'commands', 'opsx', 'explore.md')); + expect(filePath).toBe(path.join('.claude', 'commands', 'openspec', 'explore.md')); }); it('should generate correct file path for different command IDs', () => { - expect(claudeAdapter.getFilePath('new')).toBe(path.join('.claude', 'commands', 'opsx', 'new.md')); - expect(claudeAdapter.getFilePath('bulk-archive')).toBe(path.join('.claude', 'commands', 'opsx', 'bulk-archive.md')); + expect(claudeAdapter.getFilePath('new')).toBe(path.join('.claude', 'commands', 'openspec', 'new.md')); + expect(claudeAdapter.getFilePath('bulk-archive')).toBe(path.join('.claude', 'commands', 'openspec', 'bulk-archive.md')); }); it('should format file with correct YAML frontmatter', () => { @@ -74,22 +74,22 @@ describe('command-generation/adapters', () => { expect(cursorAdapter.toolId).toBe('cursor'); }); - it('should generate correct file path with opsx- prefix', () => { + it('should generate correct file path with openspec- prefix', () => { const filePath = cursorAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.cursor', 'commands', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.cursor', 'commands', 'openspec-explore.md')); }); it('should generate correct file paths for different commands', () => { - expect(cursorAdapter.getFilePath('new')).toBe(path.join('.cursor', 'commands', 'opsx-new.md')); - expect(cursorAdapter.getFilePath('bulk-archive')).toBe(path.join('.cursor', 'commands', 'opsx-bulk-archive.md')); + expect(cursorAdapter.getFilePath('new')).toBe(path.join('.cursor', 'commands', 'openspec-new.md')); + expect(cursorAdapter.getFilePath('bulk-archive')).toBe(path.join('.cursor', 'commands', 'openspec-bulk-archive.md')); }); it('should format file with Cursor-specific frontmatter', () => { const output = cursorAdapter.formatFile(sampleContent); expect(output).toContain('---\n'); - expect(output).toContain('name: /opsx-explore'); - expect(output).toContain('id: opsx-explore'); + expect(output).toContain('name: /openspec-explore'); + expect(output).toContain('id: openspec-explore'); expect(output).toContain('category: Workflow'); expect(output).toContain('description: Enter explore mode for thinking'); expect(output).toContain('---\n\n'); @@ -109,7 +109,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = windsurfAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.windsurf', 'workflows', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.windsurf', 'workflows', 'openspec-explore.md')); }); it('should format file similar to Claude format', () => { @@ -132,7 +132,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = amazonQAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.amazonq', 'prompts', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.amazonq', 'prompts', 'openspec-explore.md')); }); it('should format file with description frontmatter', () => { @@ -151,7 +151,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = antigravityAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.agent', 'workflows', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.agent', 'workflows', 'openspec-explore.md')); }); it('should format file with description frontmatter', () => { @@ -170,7 +170,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = auggieAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.augment', 'commands', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.augment', 'commands', 'openspec-explore.md')); }); it('should format file with description and argument-hint', () => { @@ -190,7 +190,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = clineAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.clinerules', 'workflows', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.clinerules', 'workflows', 'openspec-explore.md')); }); it('should format file with markdown header (no YAML frontmatter)', () => { @@ -214,7 +214,7 @@ describe('command-generation/adapters', () => { it('should generate path ending with correct structure', () => { const filePath = codexAdapter.getFilePath('explore'); - expect(filePath).toMatch(/prompts[/\\]opsx-explore\.md$/); + expect(filePath).toMatch(/prompts[/\\]openspec-explore\.md$/); }); it('should default to homedir/.codex', () => { @@ -222,7 +222,7 @@ describe('command-generation/adapters', () => { delete process.env.CODEX_HOME; try { const filePath = codexAdapter.getFilePath('explore'); - const expected = path.join(os.homedir(), '.codex', 'prompts', 'opsx-explore.md'); + const expected = path.join(os.homedir(), '.codex', 'prompts', 'openspec-explore.md'); expect(filePath).toBe(expected); } finally { if (original !== undefined) { @@ -236,7 +236,7 @@ describe('command-generation/adapters', () => { process.env.CODEX_HOME = '/custom/codex-home'; try { const filePath = codexAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join(path.resolve('/custom/codex-home'), 'prompts', 'opsx-explore.md')); + expect(filePath).toBe(path.join(path.resolve('/custom/codex-home'), 'prompts', 'openspec-explore.md')); } finally { if (original !== undefined) { process.env.CODEX_HOME = original; @@ -261,9 +261,9 @@ describe('command-generation/adapters', () => { expect(codebuddyAdapter.toolId).toBe('codebuddy'); }); - it('should generate correct file path with nested opsx folder', () => { + it('should generate correct file path with nested openspec folder', () => { const filePath = codebuddyAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.codebuddy', 'commands', 'opsx', 'explore.md')); + expect(filePath).toBe(path.join('.codebuddy', 'commands', 'openspec', 'explore.md')); }); it('should format file with name, description, and argument-hint', () => { @@ -284,13 +284,13 @@ describe('command-generation/adapters', () => { it('should generate correct file path with .prompt extension', () => { const filePath = continueAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.continue', 'prompts', 'opsx-explore.prompt')); + expect(filePath).toBe(path.join('.continue', 'prompts', 'openspec-explore.prompt')); }); it('should format file with name, description, and invokable', () => { const output = continueAdapter.formatFile(sampleContent); expect(output).toContain('---\n'); - expect(output).toContain('name: opsx-explore'); + expect(output).toContain('name: openspec-explore'); expect(output).toContain('description: Enter explore mode for thinking'); expect(output).toContain('invokable: true'); expect(output).toContain('---\n\n'); @@ -305,7 +305,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = costrictAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.cospec', 'openspec', 'commands', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.cospec', 'openspec', 'commands', 'openspec-explore.md')); }); it('should format file with description and argument-hint', () => { @@ -323,9 +323,9 @@ describe('command-generation/adapters', () => { expect(crushAdapter.toolId).toBe('crush'); }); - it('should generate correct file path with nested opsx folder', () => { + it('should generate correct file path with nested openspec folder', () => { const filePath = crushAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.crush', 'commands', 'opsx', 'explore.md')); + expect(filePath).toBe(path.join('.crush', 'commands', 'openspec', 'explore.md')); }); it('should format file with name, description, category, and tags', () => { @@ -347,7 +347,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = factoryAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.factory', 'commands', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.factory', 'commands', 'openspec-explore.md')); }); it('should format file with description and argument-hint', () => { @@ -367,7 +367,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path with .toml extension', () => { const filePath = geminiAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.gemini', 'commands', 'opsx', 'explore.toml')); + expect(filePath).toBe(path.join('.gemini', 'commands', 'openspec', 'explore.toml')); }); it('should format file in TOML format', () => { @@ -386,7 +386,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path with .prompt.md extension', () => { const filePath = githubCopilotAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.github', 'prompts', 'opsx-explore.prompt.md')); + expect(filePath).toBe(path.join('.github', 'prompts', 'openspec-explore.prompt.md')); }); it('should format file with description frontmatter', () => { @@ -405,14 +405,14 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = iflowAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.iflow', 'commands', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.iflow', 'commands', 'openspec-explore.md')); }); it('should format file with name, id, category, and description', () => { const output = iflowAdapter.formatFile(sampleContent); expect(output).toContain('---\n'); - expect(output).toContain('name: /opsx-explore'); - expect(output).toContain('id: opsx-explore'); + expect(output).toContain('name: /openspec-explore'); + expect(output).toContain('id: openspec-explore'); expect(output).toContain('category: Workflow'); expect(output).toContain('description: Enter explore mode for thinking'); expect(output).toContain('---\n\n'); @@ -427,7 +427,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = kilocodeAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.kilocode', 'workflows', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.kilocode', 'workflows', 'openspec-explore.md')); }); it('should format file without frontmatter', () => { @@ -444,7 +444,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = opencodeAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.opencode', 'commands', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.opencode', 'commands', 'openspec-explore.md')); }); it('should format file with description frontmatter', () => { @@ -458,28 +458,28 @@ describe('command-generation/adapters', () => { it('should transform colon-based command references to hyphen-based', () => { const contentWithCommands: CommandContent = { ...sampleContent, - body: 'Use /opsx:new to start, then /opsx:apply to implement.', + body: 'Use /openspec:new to start, then /openspec:apply to implement.', }; const output = opencodeAdapter.formatFile(contentWithCommands); - expect(output).toContain('/opsx-new'); - expect(output).toContain('/opsx-apply'); - expect(output).not.toContain('/opsx:new'); - expect(output).not.toContain('/opsx:apply'); + expect(output).toContain('/openspec-new'); + expect(output).toContain('/openspec-apply'); + expect(output).not.toContain('/openspec:new'); + expect(output).not.toContain('/openspec:apply'); }); it('should handle multiple command references in body', () => { const contentWithMultipleCommands: CommandContent = { ...sampleContent, - body: `/opsx:explore for ideas -/opsx:new to create -/opsx:continue to proceed -/opsx:apply to implement`, + body: `/openspec:explore for ideas +/openspec:new to create +/openspec:continue to proceed +/openspec:apply to implement`, }; const output = opencodeAdapter.formatFile(contentWithMultipleCommands); - expect(output).toContain('/opsx-explore'); - expect(output).toContain('/opsx-new'); - expect(output).toContain('/opsx-continue'); - expect(output).toContain('/opsx-apply'); + expect(output).toContain('/openspec-explore'); + expect(output).toContain('/openspec-new'); + expect(output).toContain('/openspec-continue'); + expect(output).toContain('/openspec-apply'); }); }); @@ -488,9 +488,9 @@ describe('command-generation/adapters', () => { expect(qoderAdapter.toolId).toBe('qoder'); }); - it('should generate correct file path with nested opsx folder', () => { + it('should generate correct file path with nested openspec folder', () => { const filePath = qoderAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.qoder', 'commands', 'opsx', 'explore.md')); + expect(filePath).toBe(path.join('.qoder', 'commands', 'openspec', 'explore.md')); }); it('should format file with name, description, category, and tags', () => { @@ -512,7 +512,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path with .toml extension', () => { const filePath = qwenAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.qwen', 'commands', 'opsx-explore.toml')); + expect(filePath).toBe(path.join('.qwen', 'commands', 'openspec-explore.toml')); }); it('should format file in TOML format', () => { @@ -531,12 +531,12 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = piAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.pi', 'prompts', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.pi', 'prompts', 'openspec-explore.md')); }); it('should generate correct file paths for different commands', () => { - expect(piAdapter.getFilePath('new')).toBe(path.join('.pi', 'prompts', 'opsx-new.md')); - expect(piAdapter.getFilePath('bulk-archive')).toBe(path.join('.pi', 'prompts', 'opsx-bulk-archive.md')); + expect(piAdapter.getFilePath('new')).toBe(path.join('.pi', 'prompts', 'openspec-new.md')); + expect(piAdapter.getFilePath('bulk-archive')).toBe(path.join('.pi', 'prompts', 'openspec-bulk-archive.md')); }); it('should format file with description frontmatter', () => { @@ -573,7 +573,7 @@ describe('command-generation/adapters', () => { it('should generate correct file path', () => { const filePath = roocodeAdapter.getFilePath('explore'); - expect(filePath).toBe(path.join('.roo', 'commands', 'opsx-explore.md')); + expect(filePath).toBe(path.join('.roo', 'commands', 'openspec-explore.md')); }); it('should format file with markdown header (no YAML frontmatter)', () => { @@ -590,17 +590,17 @@ describe('command-generation/adapters', () => { // path.join handles platform-specific separators const filePath = claudeAdapter.getFilePath('test'); // On any platform, path.join returns the correct separator - expect(filePath.split(path.sep)).toEqual(['.claude', 'commands', 'opsx', 'test.md']); + expect(filePath.split(path.sep)).toEqual(['.claude', 'commands', 'openspec', 'test.md']); }); it('Cursor adapter uses path.join for paths', () => { const filePath = cursorAdapter.getFilePath('test'); - expect(filePath.split(path.sep)).toEqual(['.cursor', 'commands', 'opsx-test.md']); + expect(filePath.split(path.sep)).toEqual(['.cursor', 'commands', 'openspec-test.md']); }); it('Windsurf adapter uses path.join for paths', () => { const filePath = windsurfAdapter.getFilePath('test'); - expect(filePath.split(path.sep)).toEqual(['.windsurf', 'workflows', 'opsx-test.md']); + expect(filePath.split(path.sep)).toEqual(['.windsurf', 'workflows', 'openspec-test.md']); }); it('All adapters use path.join for paths', () => { diff --git a/test/core/command-generation/generator.test.ts b/test/core/command-generation/generator.test.ts index 903aac3e1..b7a878055 100644 --- a/test/core/command-generation/generator.test.ts +++ b/test/core/command-generation/generator.test.ts @@ -28,9 +28,9 @@ describe('command-generation/generator', () => { const result = generateCommand(sampleContent, cursorAdapter); expect(result.path).toContain('.cursor'); - expect(result.path).toContain('opsx-explore.md'); - expect(result.fileContent).toContain('name: /opsx-explore'); - expect(result.fileContent).toContain('id: opsx-explore'); + expect(result.path).toContain('openspec-explore.md'); + expect(result.fileContent).toContain('name: /openspec-explore'); + expect(result.fileContent).toContain('id: openspec-explore'); expect(result.fileContent).toContain('Command body here.'); }); diff --git a/test/core/command-generation/types.test.ts b/test/core/command-generation/types.test.ts index ded7daf53..3db85c4d1 100644 --- a/test/core/command-generation/types.test.ts +++ b/test/core/command-generation/types.test.ts @@ -68,11 +68,11 @@ describe('command-generation/types', () => { describe('GeneratedCommand interface', () => { it('should represent generated command output', () => { const generated: GeneratedCommand = { - path: '.claude/commands/opsx/explore.md', + path: '.claude/commands/openspec/explore.md', fileContent: '---\nname: Test\n---\n\nBody\n', }; - expect(generated.path).toBe('.claude/commands/opsx/explore.md'); + expect(generated.path).toBe('.claude/commands/openspec/explore.md'); expect(generated.fileContent).toContain('name: Test'); }); }); diff --git a/test/core/init.test.ts b/test/core/init.test.ts index 6af92aed2..dd0911289 100644 --- a/test/core/init.test.ts +++ b/test/core/init.test.ts @@ -123,10 +123,10 @@ describe('InitCommand', () => { // Core profile: propose, explore, apply, archive const coreCommandNames = [ - 'opsx/propose.md', - 'opsx/explore.md', - 'opsx/apply.md', - 'opsx/archive.md', + 'openspec/propose.md', + 'openspec/explore.md', + 'openspec/apply.md', + 'openspec/archive.md', ]; for (const cmdName of coreCommandNames) { @@ -136,12 +136,12 @@ describe('InitCommand', () => { // Non-core commands should NOT be created const nonCoreCommandNames = [ - 'opsx/new.md', - 'opsx/continue.md', - 'opsx/ff.md', - 'opsx/sync.md', - 'opsx/bulk-archive.md', - 'opsx/verify.md', + 'openspec/new.md', + 'openspec/continue.md', + 'openspec/ff.md', + 'openspec/sync.md', + 'openspec/bulk-archive.md', + 'openspec/verify.md', ]; for (const cmdName of nonCoreCommandNames) { @@ -361,7 +361,7 @@ describe('InitCommand', () => { const initCommand = new InitCommand({ tools: 'claude', force: true }); await initCommand.execute(testDir); - const cmdFile = path.join(testDir, '.claude', 'commands', 'opsx', 'explore.md'); + const cmdFile = path.join(testDir, '.claude', 'commands', 'openspec', 'explore.md'); const content = await fs.readFile(cmdFile, 'utf-8'); // Claude commands use YAML frontmatter @@ -374,7 +374,7 @@ describe('InitCommand', () => { const initCommand = new InitCommand({ tools: 'cursor', force: true }); await initCommand.execute(testDir); - const cmdFile = path.join(testDir, '.cursor', 'commands', 'opsx-explore.md'); + const cmdFile = path.join(testDir, '.cursor', 'commands', 'openspec-explore.md'); expect(await fileExists(cmdFile)).toBe(true); const content = await fs.readFile(cmdFile, 'utf-8'); @@ -417,7 +417,7 @@ describe('InitCommand', () => { const initCommand = new InitCommand({ tools: 'gemini', force: true }); await initCommand.execute(testDir); - const cmdFile = path.join(testDir, '.gemini', 'commands', 'opsx', 'explore.toml'); + const cmdFile = path.join(testDir, '.gemini', 'commands', 'openspec', 'explore.toml'); expect(await fileExists(cmdFile)).toBe(true); const content = await fs.readFile(cmdFile, 'utf-8'); @@ -429,7 +429,7 @@ describe('InitCommand', () => { const initCommand = new InitCommand({ tools: 'windsurf', force: true }); await initCommand.execute(testDir); - const cmdFile = path.join(testDir, '.windsurf', 'workflows', 'opsx-explore.md'); + const cmdFile = path.join(testDir, '.windsurf', 'workflows', 'openspec-explore.md'); expect(await fileExists(cmdFile)).toBe(true); }); @@ -437,11 +437,11 @@ describe('InitCommand', () => { const initCommand = new InitCommand({ tools: 'continue', force: true }); await initCommand.execute(testDir); - const cmdFile = path.join(testDir, '.continue', 'prompts', 'opsx-explore.prompt'); + const cmdFile = path.join(testDir, '.continue', 'prompts', 'openspec-explore.prompt'); expect(await fileExists(cmdFile)).toBe(true); const content = await fs.readFile(cmdFile, 'utf-8'); - expect(content).toContain('name: opsx-explore'); + expect(content).toContain('name: openspec-explore'); expect(content).toContain('invokable: true'); }); @@ -449,7 +449,7 @@ describe('InitCommand', () => { const initCommand = new InitCommand({ tools: 'cline', force: true }); await initCommand.execute(testDir); - const cmdFile = path.join(testDir, '.clinerules', 'workflows', 'opsx-explore.md'); + const cmdFile = path.join(testDir, '.clinerules', 'workflows', 'openspec-explore.md'); expect(await fileExists(cmdFile)).toBe(true); }); @@ -457,7 +457,7 @@ describe('InitCommand', () => { const initCommand = new InitCommand({ tools: 'github-copilot', force: true }); await initCommand.execute(testDir); - const cmdFile = path.join(testDir, '.github', 'prompts', 'opsx-explore.prompt.md'); + const cmdFile = path.join(testDir, '.github', 'prompts', 'openspec-explore.prompt.md'); expect(await fileExists(cmdFile)).toBe(true); }); }); @@ -626,8 +626,8 @@ describe('InitCommand - profile and detection features', () => { it('should migrate commands-only extend mode to custom profile without injecting propose', async () => { await fs.mkdir(path.join(testDir, 'openspec'), { recursive: true }); - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); - await fs.writeFile(path.join(testDir, '.claude', 'commands', 'opsx', 'explore.md'), '# explore\n'); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); + await fs.writeFile(path.join(testDir, '.claude', 'commands', 'openspec', 'explore.md'), '# explore\n'); const initCommand = new InitCommand({ tools: 'claude', force: true }); await initCommand.execute(testDir); @@ -637,8 +637,8 @@ describe('InitCommand - profile and detection features', () => { expect(config.delivery).toBe('commands'); expect(config.workflows).toEqual(['explore']); - const exploreCommand = path.join(testDir, '.claude', 'commands', 'opsx', 'explore.md'); - const proposeCommand = path.join(testDir, '.claude', 'commands', 'opsx', 'propose.md'); + const exploreCommand = path.join(testDir, '.claude', 'commands', 'openspec', 'explore.md'); + const proposeCommand = path.join(testDir, '.claude', 'commands', 'openspec', 'propose.md'); expect(await fileExists(exploreCommand)).toBe(true); expect(await fileExists(proposeCommand)).toBe(false); @@ -689,7 +689,7 @@ describe('InitCommand - profile and detection features', () => { expect(await fileExists(skillFile)).toBe(true); // Commands should NOT exist - const cmdFile = path.join(testDir, '.claude', 'commands', 'opsx', 'explore.md'); + const cmdFile = path.join(testDir, '.claude', 'commands', 'openspec', 'explore.md'); expect(await fileExists(cmdFile)).toBe(false); }); @@ -708,7 +708,7 @@ describe('InitCommand - profile and detection features', () => { expect(await fileExists(skillFile)).toBe(false); // Commands should exist - const cmdFile = path.join(testDir, '.claude', 'commands', 'opsx', 'explore.md'); + const cmdFile = path.join(testDir, '.claude', 'commands', 'openspec', 'explore.md'); expect(await fileExists(cmdFile)).toBe(true); }); @@ -722,7 +722,7 @@ describe('InitCommand - profile and detection features', () => { const initCommand1 = new InitCommand({ tools: 'claude', force: true }); await initCommand1.execute(testDir); - const cmdFile = path.join(testDir, '.claude', 'commands', 'opsx', 'explore.md'); + const cmdFile = path.join(testDir, '.claude', 'commands', 'openspec', 'explore.md'); expect(await fileExists(cmdFile)).toBe(true); saveGlobalConfig({ diff --git a/test/core/legacy-cleanup.test.ts b/test/core/legacy-cleanup.test.ts index bfae37805..362abbfae 100644 --- a/test/core/legacy-cleanup.test.ts +++ b/test/core/legacy-cleanup.test.ts @@ -260,47 +260,47 @@ ${OPENSPEC_MARKERS.end}`); describe('detectLegacySlashCommands', () => { it('should detect legacy Claude slash command directory', async () => { - const dirPath = path.join(testDir, '.claude', 'commands', 'openspec'); + const dirPath = path.join(testDir, '.claude', 'commands', 'opsx'); await fs.mkdir(dirPath, { recursive: true }); await fs.writeFile(path.join(dirPath, 'proposal.md'), 'content'); const result = await detectLegacySlashCommands(testDir); - expect(result.directories).toContain('.claude/commands/openspec'); + expect(result.directories).toContain('.claude/commands/opsx'); }); it('should detect legacy Cursor slash command files', async () => { const dirPath = path.join(testDir, '.cursor', 'commands'); await fs.mkdir(dirPath, { recursive: true }); - await fs.writeFile(path.join(dirPath, 'openspec-proposal.md'), 'content'); - await fs.writeFile(path.join(dirPath, 'openspec-apply.md'), 'content'); + await fs.writeFile(path.join(dirPath, 'opsx-proposal.md'), 'content'); + await fs.writeFile(path.join(dirPath, 'opsx-apply.md'), 'content'); const result = await detectLegacySlashCommands(testDir); - expect(result.files).toContain('.cursor/commands/openspec-proposal.md'); - expect(result.files).toContain('.cursor/commands/openspec-apply.md'); + expect(result.files).toContain('.cursor/commands/opsx-proposal.md'); + expect(result.files).toContain('.cursor/commands/opsx-apply.md'); }); it('should detect legacy Windsurf workflow files', async () => { const dirPath = path.join(testDir, '.windsurf', 'workflows'); await fs.mkdir(dirPath, { recursive: true }); - await fs.writeFile(path.join(dirPath, 'openspec-archive.md'), 'content'); + await fs.writeFile(path.join(dirPath, 'opsx-archive.md'), 'content'); const result = await detectLegacySlashCommands(testDir); - expect(result.files).toContain('.windsurf/workflows/openspec-archive.md'); + expect(result.files).toContain('.windsurf/workflows/opsx-archive.md'); }); it('should detect multiple tool directories and files', async () => { // Create directory-based - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); - await fs.mkdir(path.join(testDir, '.qoder', 'commands', 'openspec'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.qoder', 'commands', 'opsx'), { recursive: true }); // Create file-based await fs.mkdir(path.join(testDir, '.cursor', 'commands'), { recursive: true }); - await fs.writeFile(path.join(testDir, '.cursor', 'commands', 'openspec-proposal.md'), 'content'); + await fs.writeFile(path.join(testDir, '.cursor', 'commands', 'opsx-proposal.md'), 'content'); const result = await detectLegacySlashCommands(testDir); - expect(result.directories).toContain('.claude/commands/openspec'); - expect(result.directories).toContain('.qoder/commands/openspec'); - expect(result.files).toContain('.cursor/commands/openspec-proposal.md'); + expect(result.directories).toContain('.claude/commands/opsx'); + expect(result.directories).toContain('.qoder/commands/opsx'); + expect(result.files).toContain('.cursor/commands/opsx-proposal.md'); }); it('should not detect non-openspec files', async () => { @@ -321,19 +321,19 @@ ${OPENSPEC_MARKERS.end}`); it('should detect TOML-based slash commands for Qwen', async () => { const dirPath = path.join(testDir, '.qwen', 'commands'); await fs.mkdir(dirPath, { recursive: true }); - await fs.writeFile(path.join(dirPath, 'openspec-proposal.toml'), 'content'); + await fs.writeFile(path.join(dirPath, 'opsx-proposal.toml'), 'content'); const result = await detectLegacySlashCommands(testDir); - expect(result.files).toContain('.qwen/commands/openspec-proposal.toml'); + expect(result.files).toContain('.qwen/commands/opsx-proposal.toml'); }); it('should detect Continue prompt files', async () => { const dirPath = path.join(testDir, '.continue', 'prompts'); await fs.mkdir(dirPath, { recursive: true }); - await fs.writeFile(path.join(dirPath, 'openspec-apply.prompt'), 'content'); + await fs.writeFile(path.join(dirPath, 'opsx-apply.prompt'), 'content'); const result = await detectLegacySlashCommands(testDir); - expect(result.files).toContain('.continue/prompts/openspec-apply.prompt'); + expect(result.files).toContain('.continue/prompts/opsx-apply.prompt'); }); it('should detect legacy OpenCode opsx-* command files', async () => { @@ -424,11 +424,11 @@ ${OPENSPEC_MARKERS.end}`); }); it('should return hasLegacyArtifacts: true when slash commands are found', async () => { - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); const result = await detectLegacyArtifacts(testDir); expect(result.hasLegacyArtifacts).toBe(true); - expect(result.slashCommandDirs).toContain('.claude/commands/openspec'); + expect(result.slashCommandDirs).toContain('.claude/commands/opsx'); }); it('should return hasLegacyArtifacts: true when openspec/AGENTS.md is found', async () => { @@ -451,14 +451,14 @@ ${OPENSPEC_MARKERS.end}`); it('should combine all detection results', async () => { // Create various legacy artifacts await fs.writeFile(path.join(testDir, 'CLAUDE.md'), `${OPENSPEC_MARKERS.start}\nContent\n${OPENSPEC_MARKERS.end}`); - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); await fs.writeFile(path.join(testDir, 'openspec', 'AGENTS.md'), 'content'); await fs.writeFile(path.join(testDir, 'openspec', 'project.md'), 'content'); const result = await detectLegacyArtifacts(testDir); expect(result.hasLegacyArtifacts).toBe(true); expect(result.configFiles).toContain('CLAUDE.md'); - expect(result.slashCommandDirs).toContain('.claude/commands/openspec'); + expect(result.slashCommandDirs).toContain('.claude/commands/opsx'); expect(result.hasOpenspecAgents).toBe(true); expect(result.hasProjectMd).toBe(true); }); @@ -500,14 +500,14 @@ ${OPENSPEC_MARKERS.end}`); }); it('should delete legacy slash command directories', async () => { - const dirPath = path.join(testDir, '.claude', 'commands', 'openspec'); + const dirPath = path.join(testDir, '.claude', 'commands', 'opsx'); await fs.mkdir(dirPath, { recursive: true }); await fs.writeFile(path.join(dirPath, 'proposal.md'), 'content'); const detection = await detectLegacyArtifacts(testDir); const result = await cleanupLegacyArtifacts(testDir, detection); - expect(result.deletedDirs).toContain('.claude/commands/openspec'); + expect(result.deletedDirs).toContain('.claude/commands/opsx'); await expect(fs.access(dirPath)).rejects.toThrow(); // Parent directory should still exist await expect(fs.access(path.join(testDir, '.claude', 'commands'))).resolves.not.toThrow(); @@ -516,13 +516,13 @@ ${OPENSPEC_MARKERS.end}`); it('should delete legacy slash command files', async () => { const dirPath = path.join(testDir, '.cursor', 'commands'); await fs.mkdir(dirPath, { recursive: true }); - const filePath = path.join(dirPath, 'openspec-proposal.md'); + const filePath = path.join(dirPath, 'opsx-proposal.md'); await fs.writeFile(filePath, 'content'); const detection = await detectLegacyArtifacts(testDir); const result = await cleanupLegacyArtifacts(testDir, detection); - expect(result.deletedFiles).toContain('.cursor/commands/openspec-proposal.md'); + expect(result.deletedFiles).toContain('.cursor/commands/opsx-proposal.md'); await expect(fs.access(filePath)).rejects.toThrow(); }); @@ -622,13 +622,13 @@ ${OPENSPEC_MARKERS.end}`); const result = { deletedFiles: [], modifiedFiles: [], - deletedDirs: ['.claude/commands/openspec'], + deletedDirs: ['.claude/commands/opsx'], projectMdNeedsMigration: false, errors: [], }; const summary = formatCleanupSummary(result); - expect(summary).toContain('✓ Removed .claude/commands/openspec/ (replaced by /opsx:*)'); + expect(summary).toContain('✓ Removed .claude/commands/opsx/ (replaced by /openspec:*)'); }); it('should format modified files', () => { @@ -749,7 +749,7 @@ ${OPENSPEC_MARKERS.end}`); const detection = { configFiles: [], configFilesToUpdate: [], - slashCommandDirs: ['.claude/commands/openspec'], + slashCommandDirs: ['.claude/commands/opsx'], slashCommandFiles: [], hasOpenspecAgents: false, hasProjectMd: false, @@ -759,7 +759,7 @@ ${OPENSPEC_MARKERS.end}`); const summary = formatDetectionSummary(detection); expect(summary).toContain('Files to remove'); - expect(summary).toContain('• .claude/commands/openspec/'); + expect(summary).toContain('• .claude/commands/opsx/'); }); it('should format slash command files', () => { @@ -767,7 +767,7 @@ ${OPENSPEC_MARKERS.end}`); configFiles: [], configFilesToUpdate: [], slashCommandDirs: [], - slashCommandFiles: ['.cursor/commands/openspec-proposal.md'], + slashCommandFiles: ['.cursor/commands/opsx-proposal.md'], hasOpenspecAgents: false, hasProjectMd: false, hasRootAgentsWithMarkers: false, @@ -776,7 +776,7 @@ ${OPENSPEC_MARKERS.end}`); const summary = formatDetectionSummary(detection); expect(summary).toContain('Files to remove'); - expect(summary).toContain('• .cursor/commands/openspec-proposal.md'); + expect(summary).toContain('• .cursor/commands/opsx-proposal.md'); }); it('should format openspec/AGENTS.md', () => { @@ -840,7 +840,7 @@ ${OPENSPEC_MARKERS.end}`); const detection = { configFiles: ['CLAUDE.md', 'CLINE.md'], configFilesToUpdate: ['CLAUDE.md', 'CLINE.md'], - slashCommandDirs: ['.claude/commands/openspec'], + slashCommandDirs: ['.claude/commands/opsx'], slashCommandFiles: [], hasOpenspecAgents: true, hasProjectMd: false, @@ -853,7 +853,7 @@ ${OPENSPEC_MARKERS.end}`); expect(summary).toContain('Files to remove'); expect(summary).toContain('Files to update'); // Check removals (only slash commands and openspec/AGENTS.md) - expect(summary).toContain('• .claude/commands/openspec/'); + expect(summary).toContain('• .claude/commands/opsx/'); expect(summary).toContain('• openspec/AGENTS.md'); // Check updates (all config files) expect(summary).toContain('• CLAUDE.md'); @@ -917,17 +917,17 @@ ${OPENSPEC_MARKERS.end}`); it('should include expected tool patterns', () => { expect(LEGACY_SLASH_COMMAND_PATHS['claude']).toEqual({ type: 'directory', - path: '.claude/commands/openspec', + path: '.claude/commands/opsx', }); expect(LEGACY_SLASH_COMMAND_PATHS['cursor']).toEqual({ type: 'files', - pattern: '.cursor/commands/openspec-*.md', + pattern: '.cursor/commands/opsx-*.md', }); expect(LEGACY_SLASH_COMMAND_PATHS['windsurf']).toEqual({ type: 'files', - pattern: '.windsurf/workflows/openspec-*.md', + pattern: '.windsurf/workflows/opsx-*.md', }); }); @@ -949,7 +949,7 @@ ${OPENSPEC_MARKERS.end}`); const detection = { configFiles: [], configFilesToUpdate: [], - slashCommandDirs: ['.claude/commands/openspec'], + slashCommandDirs: ['.claude/commands/opsx'], slashCommandFiles: [], hasOpenspecAgents: false, hasProjectMd: false, @@ -967,7 +967,7 @@ ${OPENSPEC_MARKERS.end}`); configFiles: [], configFilesToUpdate: [], slashCommandDirs: [], - slashCommandFiles: ['.cursor/commands/openspec-proposal.md'], + slashCommandFiles: ['.cursor/commands/opsx-proposal.md'], hasOpenspecAgents: false, hasProjectMd: false, hasRootAgentsWithMarkers: false, @@ -983,8 +983,8 @@ ${OPENSPEC_MARKERS.end}`); const detection = { configFiles: [], configFilesToUpdate: [], - slashCommandDirs: ['.claude/commands/openspec', '.qoder/commands/openspec'], - slashCommandFiles: ['.cursor/commands/openspec-apply.md', '.windsurf/workflows/openspec-archive.md'], + slashCommandDirs: ['.claude/commands/opsx', '.qoder/commands/opsx'], + slashCommandFiles: ['.cursor/commands/opsx-apply.md', '.windsurf/workflows/opsx-archive.md'], hasOpenspecAgents: false, hasProjectMd: false, hasRootAgentsWithMarkers: false, @@ -1005,9 +1005,9 @@ ${OPENSPEC_MARKERS.end}`); configFilesToUpdate: [], slashCommandDirs: [], slashCommandFiles: [ - '.cursor/commands/openspec-proposal.md', - '.cursor/commands/openspec-apply.md', - '.cursor/commands/openspec-archive.md', + '.cursor/commands/opsx-proposal.md', + '.cursor/commands/opsx-apply.md', + '.cursor/commands/opsx-archive.md', ], hasOpenspecAgents: false, hasProjectMd: false, @@ -1041,7 +1041,7 @@ ${OPENSPEC_MARKERS.end}`); configFiles: [], configFilesToUpdate: [], slashCommandDirs: [], - slashCommandFiles: ['.qwen/commands/openspec-proposal.toml'], + slashCommandFiles: ['.qwen/commands/opsx-proposal.toml'], hasOpenspecAgents: false, hasProjectMd: false, hasRootAgentsWithMarkers: false, @@ -1058,7 +1058,7 @@ ${OPENSPEC_MARKERS.end}`); configFiles: [], configFilesToUpdate: [], slashCommandDirs: [], - slashCommandFiles: ['.continue/prompts/openspec-apply.prompt'], + slashCommandFiles: ['.continue/prompts/opsx-apply.prompt'], hasOpenspecAgents: false, hasProjectMd: false, hasRootAgentsWithMarkers: false, @@ -1075,7 +1075,7 @@ ${OPENSPEC_MARKERS.end}`); configFiles: [], configFilesToUpdate: [], slashCommandDirs: [], - slashCommandFiles: ['.github/prompts/openspec-apply.prompt.md'], + slashCommandFiles: ['.github/prompts/opsx-apply.prompt.md'], hasOpenspecAgents: false, hasProjectMd: false, hasRootAgentsWithMarkers: false, diff --git a/test/core/migration.test.ts b/test/core/migration.test.ts index 409206e94..75ba7dc55 100644 --- a/test/core/migration.test.ts +++ b/test/core/migration.test.ts @@ -137,7 +137,7 @@ describe('migration', () => { it('ignores unknown custom skill and command files when scanning workflows', async () => { await writeSkill(projectDir, 'my-custom-skill'); - const customCommandPath = path.join(projectDir, '.claude', 'commands', 'opsx', 'my-custom.md'); + const customCommandPath = path.join(projectDir, '.claude', 'commands', 'openspec', 'my-custom.md'); await fsp.mkdir(path.dirname(customCommandPath), { recursive: true }); await fsp.writeFile(customCommandPath, '# custom\n', 'utf-8'); diff --git a/test/core/shared/skill-generation.test.ts b/test/core/shared/skill-generation.test.ts index 6c755f51d..f6baf54bc 100644 --- a/test/core/shared/skill-generation.test.ts +++ b/test/core/shared/skill-generation.test.ts @@ -260,28 +260,28 @@ describe('skill-generation', () => { const template = { name: 'transform-test', description: 'Test transform callback', - instructions: 'Use /opsx:new to start and /opsx:apply to implement.', + instructions: 'Use /openspec:new to start and /openspec:apply to implement.', }; - const transformer = (text: string) => text.replace(/\/opsx:/g, '/opsx-'); + const transformer = (text: string) => text.replace(/\/openspec:/g, '/openspec-'); const content = generateSkillContent(template, '0.23.0', transformer); - expect(content).toContain('/opsx-new'); - expect(content).toContain('/opsx-apply'); - expect(content).not.toContain('/opsx:new'); - expect(content).not.toContain('/opsx:apply'); + expect(content).toContain('/openspec-new'); + expect(content).toContain('/openspec-apply'); + expect(content).not.toContain('/openspec:new'); + expect(content).not.toContain('/openspec:apply'); }); it('should not transform instructions when callback is undefined', () => { const template = { name: 'no-transform-test', description: 'Test without transform', - instructions: 'Use /opsx:new to start.', + instructions: 'Use /openspec:new to start.', }; const content = generateSkillContent(template, '0.23.0', undefined); - expect(content).toContain('/opsx:new'); + expect(content).toContain('/openspec:new'); }); it('should support custom transformInstructions logic', () => { diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index f8fb1307b..4bca7c6ed 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -30,43 +30,43 @@ import { import { generateSkillContent } from '../../../src/core/shared/skill-generation.js'; const EXPECTED_FUNCTION_HASHES: Record = { - getExploreSkillTemplate: '55a2a1afcba0af88c638e77e4e3870f65ed82c030b4a2056d39812ae13a616be', + getExploreSkillTemplate: 'ad8aa016932952a9295c54ef592447f03fac1695905bfebeb8a2ea54fc3303fc', getNewChangeSkillTemplate: '5989672758eccf54e3bb554ab97f2c129a192b12bbb7688cc1ffcf6bccb1ae9d', getContinueChangeSkillTemplate: 'f2e413f0333dfd6641cc2bd1a189273fdea5c399eecdde98ef528b5216f097b3', - getApplyChangeSkillTemplate: '26e52e67693e93fbcdd40dcd3e20949c07ce019183d55a8149d0260c791cd7f4', - getFfChangeSkillTemplate: 'a7332fb14c8dc3f9dec71f5d332790b4a8488191e7db4ab6132ccbefecf9ded9', + getApplyChangeSkillTemplate: '48a9173d8b201f9db955e40fa53d03824eaa1a8b26529dd00b5bd00cd3e5fbe9', + getFfChangeSkillTemplate: '3bc8740c011bbf7f124db4eed3e96ca76ed1a4aa54f96674e8c57d47354f7b18', getSyncSpecsSkillTemplate: 'bded184e4c345619148de2c0ad80a5b527d4ffe45c87cc785889b9329e0f465b', - getOnboardSkillTemplate: '819a2d117ad1386187975686839cb0584b41484013d0ca6a6691f7a439a11a4a', - getOpsxExploreCommandTemplate: '91353d9e8633a3a9ce7339e796f1283478fca279153f3807c92f4f8ece246b19', - getOpsxNewCommandTemplate: '62eee32d6d81a376e7be845d0891e28e6262ad07482f9bfe6af12a9f0366c364', - getOpsxContinueCommandTemplate: '8bbaedcc95287f9e822572608137df4f49ad54cedfb08d3342d0d1c4e9716caa', - getOpsxApplyCommandTemplate: 'a9d631a07fcd832b67d263ff3800b98604ab8d378baf1b0d545907ef3affa3b5', - getOpsxFfCommandTemplate: 'cdebe872cc8e0fcc25c8864b98ffd66a93484c0657db94bd1285b8113092702a', + getOnboardSkillTemplate: '2ff347ce9f2724f7ab02111ffd5f7e00d34512a654070e3c4ce77d993725dbf6', + getOpsxExploreCommandTemplate: 'd38a064445b077d63135f544a4fdcc4c01bb3e77e451403ea16d099b0a6a6c0f', + getOpsxNewCommandTemplate: 'f800f4635b7381ec392ca2b140fe74fd2c9be947f23367214a8f69640b58013f', + getOpsxContinueCommandTemplate: 'e1ca122fd6a92243c4b7f91349b457bd1e904cad9c84ffa938b334eceb379525', + getOpsxApplyCommandTemplate: '02cdb27b54c520309aeeeac44a1bb99e540c7a4ae7ec064614ce5939b7305de0', + getOpsxFfCommandTemplate: '64e3d0830998cebc1f7b428a3822aa785dadd6bf384cda097d80f2f464463a14', getArchiveChangeSkillTemplate: '6f8ca383fdb5a4eb9872aca81e07bf0ba7f25e4de8617d7a047ca914ca7f14b9', getBulkArchiveChangeSkillTemplate: 'b40fc44ea4e420bdc9c803985b10e5c091fc472cdfc69153b962be6be303bddd', - getOpsxSyncCommandTemplate: '378d035fe7cc30be3e027b66dcc4b8afc78ef1c8369c39479c9b05a582fb5ccf', + getOpsxSyncCommandTemplate: 'd557936427ee9efb6ee39c27c657892ac645e71c4eda3d889de28df079f9ac67', getVerifyChangeSkillTemplate: '63a213ba3b42af54a1cd56f5072234a03b265c3fe4a1da12cd6fbbef5ee46c4b', - getOpsxArchiveCommandTemplate: 'b44cc9748109f61687f9f596604b037bc3ea803abc143b22f09a76aebd98b493', - getOpsxOnboardCommandTemplate: '10052d05a4e2cdade7fdfa549b3444f7a92f55a39bf81ddd6af7e0e9e83a7302', - getOpsxBulkArchiveCommandTemplate: 'eaaba253a950b9e681d8427a5cbc6b50c4e91137fb37fd2360859e08f63a0c14', - getOpsxVerifyCommandTemplate: '9b4d3ca422553b7534764eb3a009da87a051612c5238e9baab294c7b1233e9a2', - getOpsxProposeSkillTemplate: 'd67f937d44650e9c61d2158c865309fbab23cb3f50a3d4868a640a97776e3999', - getOpsxProposeCommandTemplate: '41ad59b37eafd7a161bab5c6e41997a37368f9c90b194451295ede5cd42e4d46', + getOpsxArchiveCommandTemplate: '6d17674f9fa2c9b2bdcf64732b965534e329139cbdd324f450b960751a593d7b', + getOpsxOnboardCommandTemplate: '3aaed5d2d7388790233b5a348581c18081ad379856c22c9aa3097155676660c9', + getOpsxBulkArchiveCommandTemplate: '16fcb156a9a50078e834261b4d0ddce1e33b8e791bedf34596e15fdabf91a930', + getOpsxVerifyCommandTemplate: 'ba0d9f9b679ae5585dfdce7535cc98e52d6f02cf66c4d468976f1ef1ea32892d', + getOpsxProposeSkillTemplate: '769f79aa2addf577f6acadde5b45618773192a1438daea59a5857bb800252841', + getOpsxProposeCommandTemplate: '675793c7bcabbe4d91b139ee34c784bc91074a8b1741808323b90c2093378876', getFeedbackSkillTemplate: 'd7d83c5f7fc2b92fe8f4588a5bf2d9cb315e4c73ec19bcd5ef28270906319a0d', }; const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { - 'openspec-explore': '90463d00761417dfbca5cb09361adcf8bbdbbb24000b86dd03647869a4104479', + 'openspec-explore': '0f9be2435de07841ae89969335206319418ffa302557dae7ae281026638e306e', 'openspec-new-change': 'c324a7ace1f244aa3f534ac8e3370a2c11190d6d1b85a315f26a211398310f0f', 'openspec-continue-change': '463cf0b980ec9c3c24774414ef2a3e48e9faa8577bc8748990f45ab3d5efe960', - 'openspec-apply-change': 'a0084442b59be9d7e22a0382a279d470501e1ecf74bdd5347e169951c9be191c', - 'openspec-ff-change': '672c3a5b8df152d959b15bd7ae2be7a75ab7b8eaa2ec1e0daa15c02479b27937', + 'openspec-apply-change': '0d2b9402da87dad71e956b669d61f4e58db7a93f760cfffb314831be06549578', + 'openspec-ff-change': '5905fbf9aa1aaadc704dec4a10be1b724b68ac4a5373e0866e56a8822d7db689', 'openspec-sync-specs': 'b8859cf454379a19ca35dbf59eedca67306607f44a355327f9dc851114e50bde', 'openspec-archive-change': 'f83c85452bd47de0dee6b8efbcea6a62534f8a175480e9044f3043f887cebf0f', 'openspec-bulk-archive-change': 'a235a539f7729ab7669e45256905808789240ecd02820e044f4d0eef67b0c2ab', 'openspec-verify-change': '30d07c6f7051965f624f5964db51844ec17c7dfd05f0da95281fe0ca73616326', - 'openspec-onboard': 'dbce376cf895f3fe4f63b4bce66d258c35b7b8884ac746670e5e35fabcefd255', - 'openspec-propose': '20e36dabefb90e232bad0667292bd5007ec280f8fc4fc995dbc4282bf45a22e7', + 'openspec-onboard': '435b5678b19c7cf083880f13ada549e3168a96128beff90f29ae8db44a0da05c', + 'openspec-propose': '8e01fd8ee8f9568a111b0a53780befb5e79da304cfa32c2437cd078da96880d6', }; function stableStringify(value: unknown): string { diff --git a/test/core/update.test.ts b/test/core/update.test.ts index c36ac3da8..fc1d0d0e8 100644 --- a/test/core/update.test.ts +++ b/test/core/update.test.ts @@ -193,7 +193,7 @@ Old instructions content }); describe('command updates', () => { - it('should update opsx commands for configured Claude tool', async () => { + it('should update openspec commands for configured Claude tool', async () => { // Set up a configured Claude tool const skillsDir = path.join(testDir, '.claude', 'skills'); await fs.mkdir(path.join(skillsDir, 'openspec-explore'), { @@ -206,8 +206,8 @@ Old instructions content await updateCommand.execute(testDir); - // Check opsx command files were created - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + // Check openspec command files were created + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); const exploreCmd = path.join(commandsDir, 'explore.md'); const exists = await FileSystemUtils.fileExists(exploreCmd); expect(exists).toBe(true); @@ -220,7 +220,7 @@ Old instructions content expect(content).toContain('tags:'); }); - it('should update core profile opsx commands when tool is configured', async () => { + it('should update core profile openspec commands when tool is configured', async () => { // Set up a configured tool const skillsDir = path.join(testDir, '.claude', 'skills'); await fs.mkdir(path.join(skillsDir, 'openspec-explore'), { @@ -235,7 +235,7 @@ Old instructions content // Verify core profile commands were created (propose, explore, apply, archive) const coreCommandIds = ['explore', 'apply', 'archive', 'propose']; - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); for (const cmdId of coreCommandIds) { const cmdFile = path.join(commandsDir, `${cmdId}.md`); const exists = await FileSystemUtils.fileExists(cmdFile); @@ -313,12 +313,12 @@ Old instructions content await updateCommand.execute(testDir); - // Check Qwen command format (TOML) - Qwen uses flat path structure: opsx-.toml + // Check Qwen command format (TOML) - Qwen uses flat path structure: openspec-.toml const qwenCmd = path.join( testDir, '.qwen', 'commands', - 'opsx-explore.toml' + 'openspec-explore.toml' ); const exists = await FileSystemUtils.fileExists(qwenCmd); expect(exists).toBe(true); @@ -346,7 +346,7 @@ Old instructions content testDir, '.windsurf', 'workflows', - 'opsx-explore.md' + 'openspec-explore.md' ); const exists = await FileSystemUtils.fileExists(windsurfCmd); expect(exists).toBe(true); @@ -963,7 +963,7 @@ ${OPENSPEC_MARKERS.end} ); // Create legacy slash command directory - const legacyCommandDir = path.join(testDir, '.claude', 'commands', 'openspec'); + const legacyCommandDir = path.join(testDir, '.claude', 'commands', 'opsx'); await fs.mkdir(legacyCommandDir, { recursive: true }); await fs.writeFile( path.join(legacyCommandDir, 'old-command.md'), @@ -978,7 +978,7 @@ ${OPENSPEC_MARKERS.end} // Should show cleanup message for directory expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Removed .claude/commands/openspec/') + expect.stringContaining('Removed .claude/commands/opsx/') ); // Legacy directory should be deleted @@ -1113,7 +1113,7 @@ More user content after markers. describe('legacy tool upgrade', () => { it('should upgrade legacy tools to new skills with --force', async () => { // Create legacy slash command directory (no skills exist yet) - const legacyCommandDir = path.join(testDir, '.claude', 'commands', 'openspec'); + const legacyCommandDir = path.join(testDir, '.claude', 'commands', 'opsx'); await fs.mkdir(legacyCommandDir, { recursive: true }); await fs.writeFile( path.join(legacyCommandDir, 'proposal.md'), @@ -1141,7 +1141,7 @@ More user content after markers. expect.stringContaining('Getting started') ); expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('/opsx:new') + expect.stringContaining('/openspec:new') ); // Skills should be created @@ -1158,15 +1158,15 @@ More user content after markers. it('should upgrade multiple legacy tools with --force', async () => { // Create legacy command directories for Claude and Cursor - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); await fs.writeFile( - path.join(testDir, '.claude', 'commands', 'openspec', 'proposal.md'), + path.join(testDir, '.claude', 'commands', 'opsx', 'proposal.md'), 'content' ); await fs.mkdir(path.join(testDir, '.cursor', 'commands'), { recursive: true }); await fs.writeFile( - path.join(testDir, '.cursor', 'commands', 'openspec-proposal.md'), + path.join(testDir, '.cursor', 'commands', 'opsx-proposal.md'), 'content' ); @@ -1201,7 +1201,7 @@ More user content after markers. ); // Also create legacy directory (simulating partial upgrade) - const legacyCommandDir = path.join(testDir, '.claude', 'commands', 'openspec'); + const legacyCommandDir = path.join(testDir, '.claude', 'commands', 'opsx'); await fs.mkdir(legacyCommandDir, { recursive: true }); await fs.writeFile( path.join(legacyCommandDir, 'proposal.md'), @@ -1216,7 +1216,7 @@ More user content after markers. // Legacy cleanup should happen expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Removed .claude/commands/openspec/') + expect.stringContaining('Removed .claude/commands/opsx/') ); // Should NOT show "Tools detected from legacy artifacts" because claude is already configured @@ -1246,15 +1246,15 @@ More user content after markers. ); // Create legacy commands for both Claude (configured) and Cursor (not configured) - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); await fs.writeFile( - path.join(testDir, '.claude', 'commands', 'openspec', 'proposal.md'), + path.join(testDir, '.claude', 'commands', 'opsx', 'proposal.md'), 'content' ); await fs.mkdir(path.join(testDir, '.cursor', 'commands'), { recursive: true }); await fs.writeFile( - path.join(testDir, '.cursor', 'commands', 'openspec-proposal.md'), + path.join(testDir, '.cursor', 'commands', 'opsx-proposal.md'), 'content' ); @@ -1308,9 +1308,9 @@ More user content after markers. it('should create only effective profile skills when upgrading legacy tools', async () => { // Create legacy command directory - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); await fs.writeFile( - path.join(testDir, '.claude', 'commands', 'openspec', 'proposal.md'), + path.join(testDir, '.claude', 'commands', 'opsx', 'proposal.md'), 'content' ); @@ -1339,9 +1339,9 @@ More user content after markers. it('should create commands when upgrading legacy tools', async () => { // Create legacy command directory - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); await fs.writeFile( - path.join(testDir, '.claude', 'commands', 'openspec', 'proposal.md'), + path.join(testDir, '.claude', 'commands', 'opsx', 'proposal.md'), 'content' ); @@ -1349,8 +1349,8 @@ More user content after markers. const forceUpdateCommand = new UpdateCommand({ force: true }); await forceUpdateCommand.execute(testDir); - // New opsx commands should be created - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + // New openspec commands should be created + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); const exploreCmd = path.join(commandsDir, 'explore.md'); const exists = await FileSystemUtils.fileExists(exploreCmd); expect(exists).toBe(true); @@ -1364,9 +1364,9 @@ More user content after markers. workflows: ['explore'], }); - await fs.mkdir(path.join(testDir, '.claude', 'commands', 'openspec'), { recursive: true }); + await fs.mkdir(path.join(testDir, '.claude', 'commands', 'opsx'), { recursive: true }); await fs.writeFile( - path.join(testDir, '.claude', 'commands', 'openspec', 'proposal.md'), + path.join(testDir, '.claude', 'commands', 'opsx', 'proposal.md'), 'content' ); @@ -1381,7 +1381,7 @@ More user content after markers. path.join(skillsDir, 'openspec-propose', 'SKILL.md') )).toBe(false); - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); expect(await FileSystemUtils.fileExists( path.join(commandsDir, 'explore.md') )).toBe(true); @@ -1444,7 +1444,7 @@ More user content after markers. )).toBe(true); // Commands should NOT be created - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); expect(await FileSystemUtils.fileExists( path.join(commandsDir, 'explore.md') )).toBe(false); @@ -1464,7 +1464,7 @@ More user content after markers. await updateCommand.execute(testDir); // Commands should be created - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); expect(await FileSystemUtils.fileExists( path.join(commandsDir, 'explore.md') )).toBe(true); @@ -1523,7 +1523,7 @@ content ` ); - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); await fs.mkdir(commandsDir, { recursive: true }); await fs.writeFile(path.join(commandsDir, 'explore.md'), 'old command'); @@ -1542,7 +1542,7 @@ content delivery: 'commands', }); - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); await fs.mkdir(commandsDir, { recursive: true }); await fs.writeFile(path.join(commandsDir, 'explore.md'), 'existing command'); @@ -1583,7 +1583,7 @@ content // Add a non-core workflow await fs.mkdir(path.join(skillsDir, 'openspec-new-change'), { recursive: true }); await fs.writeFile(path.join(skillsDir, 'openspec-new-change', 'SKILL.md'), 'old'); - const extraCommandFile = path.join(testDir, '.claude', 'commands', 'opsx', 'new.md'); + const extraCommandFile = path.join(testDir, '.claude', 'commands', 'openspec', 'new.md'); await fs.mkdir(path.dirname(extraCommandFile), { recursive: true }); await fs.writeFile(extraCommandFile, 'old'); @@ -1739,7 +1739,7 @@ content }); it('should detect installed workflows from managed command files', async () => { - const commandsDir = path.join(testDir, '.claude', 'commands', 'opsx'); + const commandsDir = path.join(testDir, '.claude', 'commands', 'openspec'); await fs.mkdir(commandsDir, { recursive: true }); await fs.writeFile(path.join(commandsDir, 'explore.md'), 'content'); diff --git a/test/utils/command-references.test.ts b/test/utils/command-references.test.ts index c7ff2ed85..3429c4b78 100644 --- a/test/utils/command-references.test.ts +++ b/test/utils/command-references.test.ts @@ -4,24 +4,24 @@ import { transformToHyphenCommands } from '../../src/utils/command-references.js describe('transformToHyphenCommands', () => { describe('basic transformations', () => { it('should transform single command reference', () => { - expect(transformToHyphenCommands('/opsx:new')).toBe('/opsx-new'); + expect(transformToHyphenCommands('/openspec:new')).toBe('/openspec-new'); }); it('should transform multiple command references', () => { - const input = '/opsx:new and /opsx:apply'; - const expected = '/opsx-new and /opsx-apply'; + const input = '/openspec:new and /openspec:apply'; + const expected = '/openspec-new and /openspec-apply'; expect(transformToHyphenCommands(input)).toBe(expected); }); it('should transform command reference in context', () => { - const input = 'Use /opsx:apply to implement tasks'; - const expected = 'Use /opsx-apply to implement tasks'; + const input = 'Use /openspec:apply to implement tasks'; + const expected = 'Use /openspec-apply to implement tasks'; expect(transformToHyphenCommands(input)).toBe(expected); }); it('should handle backtick-quoted commands', () => { - const input = 'Run `/opsx:continue` to proceed'; - const expected = 'Run `/opsx-continue` to proceed'; + const input = 'Run `/openspec:continue` to proceed'; + const expected = 'Run `/openspec-continue` to proceed'; expect(transformToHyphenCommands(input)).toBe(expected); }); }); @@ -42,20 +42,20 @@ describe('transformToHyphenCommands', () => { }); it('should handle multiple occurrences on same line', () => { - const input = '/opsx:new /opsx:continue /opsx:apply'; - const expected = '/opsx-new /opsx-continue /opsx-apply'; + const input = '/openspec:new /openspec:continue /openspec:apply'; + const expected = '/openspec-new /openspec-continue /openspec-apply'; expect(transformToHyphenCommands(input)).toBe(expected); }); }); describe('multiline content', () => { it('should transform references across multiple lines', () => { - const input = `Use /opsx:new to start -Then /opsx:continue to proceed -Finally /opsx:apply to implement`; - const expected = `Use /opsx-new to start -Then /opsx-continue to proceed -Finally /opsx-apply to implement`; + const input = `Use /openspec:new to start +Then /openspec:continue to proceed +Finally /openspec:apply to implement`; + const expected = `Use /openspec-new to start +Then /openspec-continue to proceed +Finally /openspec-apply to implement`; expect(transformToHyphenCommands(input)).toBe(expected); }); }); @@ -75,8 +75,8 @@ Finally /opsx-apply to implement`; ]; for (const cmd of commands) { - it(`should transform /opsx:${cmd}`, () => { - expect(transformToHyphenCommands(`/opsx:${cmd}`)).toBe(`/opsx-${cmd}`); + it(`should transform /openspec:${cmd}`, () => { + expect(transformToHyphenCommands(`/openspec:${cmd}`)).toBe(`/openspec-${cmd}`); }); } });