{"owner":"halo-dev","repo":"halo","hasSkills":true,"totalSkillsCount":39,"totalTokensCount":49680,"categories":["command-prompt","anthropic-skill","root-instruction","claude-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".claude/commands/opsx/apply.md",".claude/commands/opsx/archive.md",".claude/commands/opsx/explore.md",".claude/commands/opsx/propose.md",".claude/commands/opsx/sync.md",".claude/commands/opsx/update.md",".claude/skills/gradle-dependency-updates/SKILL.md",".claude/skills/openspec-apply-change/SKILL.md",".claude/skills/openspec-archive-change/SKILL.md",".claude/skills/openspec-explore/SKILL.md",".claude/skills/openspec-propose/SKILL.md",".claude/skills/openspec-sync-specs/SKILL.md",".claude/skills/openspec-update-change/SKILL.md",".codex/skills/openspec-apply-change/SKILL.md",".codex/skills/openspec-archive-change/SKILL.md",".codex/skills/openspec-explore/SKILL.md",".codex/skills/openspec-propose/SKILL.md",".codex/skills/openspec-sync-specs/SKILL.md",".codex/skills/openspec-update-change/SKILL.md",".github/skills/openspec-apply-change/SKILL.md",".github/skills/openspec-archive-change/SKILL.md",".github/skills/openspec-explore/SKILL.md",".github/skills/openspec-propose/SKILL.md",".github/skills/openspec-sync-specs/SKILL.md",".github/skills/openspec-update-change/SKILL.md","AGENTS.md","CLAUDE.md","api/AGENTS.md","api/CLAUDE.md","application/AGENTS.md","application/CLAUDE.md","ui/AGENTS.md","ui/CLAUDE.md","ui/packages/api-client/README.md","ui/packages/console-shared/README.md","ui/packages/editor/README.md","ui/packages/editor/docs/extension.md","ui/packages/editor/docs/runtime-metadata.md","ui/packages/ui-plugin-bundler-kit/README.md"],"skills":{".claude/commands/opsx/apply.md":"---\nname: \"OPSX: Apply\"\ndescription: Implement tasks from an OpenSpec change (Experimental)\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using `/opsx:continue`\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! You can archive this change with `/opsx:archive`.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n",".claude/commands/opsx/archive.md":"---\nname: \"OPSX: Archive\"\ndescription: Archive a completed change in the experimental workflow\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, archive, experimental]\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Prompt user for confirmation to continue\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Prompt user for confirmation to continue\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Spec sync status (synced / sync skipped / no delta specs)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs\n\nAll artifacts complete. All tasks complete.\n```\n\n**Output On Success (No Delta Specs)**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** No delta specs\n\nAll artifacts complete. All tasks complete.\n```\n\n**Output On Success With Warnings**\n\n```\n## Archive Complete (with warnings)\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** Sync skipped (user chose to skip)\n\n**Warnings:**\n- Archived with 2 incomplete artifacts\n- Archived with 3 incomplete tasks\n- Delta spec sync was skipped (user chose to skip)\n\nReview the archive if this was not intentional.\n```\n\n**Output On Error (Archive Exists)**\n\n```\n## Archive Failed\n\n**Change:** <change-name>\n**Target:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n\nTarget archive directory already exists.\n\n**Options:**\n1. Rename the existing archive\n2. Delete the existing archive if it's a duplicate\n3. Wait until a different date to archive\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use the Skill tool to invoke `openspec-sync-specs` (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n",".claude/commands/opsx/explore.md":"---\nname: \"OPSX: Explore\"\ndescription: \"Enter explore mode - think through ideas, investigate problems, clarify requirements\"\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, explore, experimental, thinking]\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The argument after `/opsx:explore` is whatever the user wants to think about. Could be:\n- A vague idea: \"real-time collaboration\"\n- A specific problem: \"the auth system is getting unwieldy\"\n- A change name: \"add-dark-mode\" (to explore in context of that change)\n- A comparison: \"postgres vs sqlite for this\"\n- Nothing (just enter explore mode)\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\nIf the user mentioned a specific change name, read its artifacts for context.\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen things crystallize, you might offer a summary - but it's optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n",".claude/commands/opsx/propose.md":"---\nname: \"OPSX: Propose\"\ndescription: Propose a new change - create it and generate all artifacts in one step\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The argument after `/opsx:propose` is the change name (kebab-case), OR a description of what the user wants to build.\n\n**Steps**\n\n1. **If no input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` to start implementing.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n",".claude/commands/opsx/sync.md":"---\nname: \"OPSX: Sync\"\ndescription: Sync delta specs from a change to main specs\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, specs, experimental]\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n",".claude/commands/opsx/update.md":"---\nname: \"OPSX: Update\"\ndescription: Update a change - revise existing planning artifacts and keep them coherent (Experimental)\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name after `/opsx:update` (e.g., `/opsx:update add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n",".claude/skills/gradle-dependency-updates/SKILL.md":"---\nname: gradle-dependency-updates\ndescription: >-\n  Upgrade outdated Gradle dependencies in this project. Use this skill whenever\n  the user asks to check for dependency updates, show what's outdated, or\n  upgrade/bump/update Gradle dependencies, libraries, or plugins — including\n  mentions of \"dependencyUpdates\", the versions plugin, or \"outdated\n  dependencies\". Backend only (api/application/platform); frontend dependencies\n  (ui/, pnpm) are out of scope.\n---\n\n# Gradle Dependency Updates\n\nThis project manages backend dependencies through a Gradle version catalog\n(`gradle/libs.versions.toml`) plus the Spring Boot BOM. The ben-manes Versions\nplugin is already applied to `:api` and `:application`.\n\n## 1. Find what's outdated\n\n```bash\n./gradlew dependencyUpdates -DoutputFormatter=plain,json --console=plain\n```\n\nRead the reports at `api/build/dependencyUpdates/report.{txt,json}` and\n`application/build/dependencyUpdates/report.{txt,json}`; merge and dedupe\nacross the two modules.\n\nThe plugin's default revision includes stable releases, so \"later milestone\nversions\" does not mean pre-releases — only versions with a qualifier\n(alpha/beta/rc) are pre-releases.\n\n## 2. Decide what can be bumped\n\n- **Catalog library** — declared in `[libraries]` of `gradle/libs.versions.toml`\n  → bumpable in the catalog.\n- **Gradle plugin** — declared in `[plugins]` → bumpable in the catalog.\n- **BOM-managed** — pinned by the Spring Boot BOM in `platform/application`\n  (Spring Framework, r2dbc drivers, postgresql, byte-buddy, jspecify,\n  junit-platform-launcher, ...) → never bump individually; they arrive with the\n  next Spring Boot BOM bump.\n- **Gradle wrapper** — `gradle/wrapper/gradle-wrapper.properties`.\n\n## 3. Project constraints (do not cross)\n\n- **thymeleaf**: the `runtimeOnly ':thymeleaf:...'` flat-dir jars in\n  `application/build.gradle` are a pinned workaround (halo-dev/halo#7289) —\n  never touch them.\n- **lombok**: keep aligned with the `io.freefair.lombok` plugin; do not add a\n  `lombok { version = ... }` override.\n- **ben-manes Versions plugin itself**: stay on 0.54.0 — 0.59.0 fails to apply\n  with a classloader error.\n- **Pre-releases** (e.g. tika-core 4.0.0-beta-1): skip unless the user asks.\n- Versions live only in `gradle/libs.versions.toml`, `gradle.properties`, or\n  `ui/package.json` — never hard-code them (AGENTS.md).\n\n## 4. Show the update plan\n\nGroup by category, current → latest with the semver delta (major/minor/patch),\nstate where each bump lands, and list excluded items with reasons.\n\n## 5. Apply and verify\n\nEdit `gradle/libs.versions.toml` — one line per bump — only after the user\napproves the plan, then confirm the build:\n\n```bash\n./gradlew build\n```\n\nRe-run `dependencyUpdates` and check that the bumped items no longer appear as\noutdated; what remains should be exactly the out-of-scope set.\n",".claude/skills/openspec-apply-change/SKILL.md":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n",".claude/skills/openspec-archive-change/SKILL.md":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n",".claude/skills/openspec-explore/SKILL.md":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n",".claude/skills/openspec-propose/SKILL.md":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n",".claude/skills/openspec-sync-specs/SKILL.md":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n",".claude/skills/openspec-update-change/SKILL.md":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n",".codex/skills/openspec-apply-change/SKILL.md":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n",".codex/skills/openspec-archive-change/SKILL.md":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n",".codex/skills/openspec-explore/SKILL.md":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n",".codex/skills/openspec-propose/SKILL.md":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n",".codex/skills/openspec-sync-specs/SKILL.md":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n",".codex/skills/openspec-update-change/SKILL.md":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n",".github/skills/openspec-apply-change/SKILL.md":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n",".github/skills/openspec-archive-change/SKILL.md":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n",".github/skills/openspec-explore/SKILL.md":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n",".github/skills/openspec-propose/SKILL.md":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n",".github/skills/openspec-sync-specs/SKILL.md":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n",".github/skills/openspec-update-change/SKILL.md":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n","AGENTS.md":"# Repository Guidelines\n\nHalo is a full-stack monorepo: Java 21 / Spring Boot WebFlux + R2DBC backend and a Vue 3 + TypeScript + TailwindCSS frontend. Work from the repo root and load **every relevant module guide** for the areas you touch.\n\n## Modules\n\n|       Module        |                           Path                            |\n|---------------------|-----------------------------------------------------------|\n| API library         | `api/` (extension model, contracts, security)             |\n| Backend application | `application/` (services, routers, migrations, packaging) |\n| Frontend            | `ui/` (console, user center, workspace packages)          |\n| Application BOM     | `platform/application/` (shared dependency constraints)   |\n| Plugin BOM          | `platform/plugin/` (plugin-facing constraints)            |\n\n## Quick Commands\n\nRun from the repo root:\n\n- `./gradlew build` — full backend build and tests.\n- `./gradlew spotlessApply` — format Java, Markdown, JSON, and properties.\n- `./gradlew :application:test` — backend tests.\n- `pnpm -C ui typecheck && pnpm -C ui lint` — frontend validation.\n- `pnpm -C ui test:unit` — frontend unit tests.\n- `./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen` — regenerate the UI client after contract changes.\n\n## Cross-Module Rules\n\n- Keep API contracts, backend handlers, and UI usage in sync; never hand-edit `ui/packages/api-client/src/` — regenerate it after contract changes.\n- Versions live in `gradle/libs.versions.toml`, `gradle.properties`, or `ui/package.json`; never hard-code them.\n- Branch naming: `feat/`, `fix/`, `improvement/`, `upgrade/`; PRs target `upstream` and follow `.github/pull_request_template.md`.\n- Stage specific files only; never `git add -A` (H2 artifacts and build outputs appear under `application/`).\n- Add tests for changed behavior; keep CI green.\n- Ask before public API changes, new dependencies, database migrations, security configuration, or CI workflow changes.\n- Never commit secrets or introduce blocking I/O into reactive flows.\n\n","CLAUDE.md":"@AGENTS.md\n","api/AGENTS.md":"# API Library Guidelines\n\n`api/` is the public API library: the extension model, service contracts, and security abstractions consumed by the application and by plugins. Changes here affect downstream compatibility.\n\n## Scope\n\n- Source: `api/src/main/java/run/halo/app/` (core, extension, plugin, security, migration, theme, and related packages).\n- Tests: `api/src/test/`.\n\n## Commands\n\n- `./gradlew :api:test` — run API library tests.\n- `./gradlew spotlessApply` — format Java and Markdown.\n\n## Conventions\n\n- Java 21, 4-space indentation, formatted with Spotless.\n- The API is a compatibility surface: prefer additive changes, and flag any breaking signature or contract change for review before merging.\n- Extension definitions and schemas live in `application/src/main/resources/extensions/`; when you change them, update `application/` and `ui/` together.\n- Tests: JUnit 5, named `XxxTest` (unit) or `XxxIntegrationTest` (integration).\n\n## Boundaries\n\n- Public API changes in `api/` require explicit approval — treat them as affecting every plugin built against Halo.\n\n","api/CLAUDE.md":"@AGENTS.md\n","application/AGENTS.md":"# Backend Application Guidelines\n\n`application/` is the WebFlux + R2DBC backend: services, routers, security, migrations, and packaging. Everything here is reactive — never block threads.\n\n## Structure\n\n- `application/src/main/java/` — services, routers, endpoints, and configuration.\n- `application/src/main/resources/application*.yaml` — environment configuration.\n- `application/src/main/resources/db/migration/{h2,mariadb,mysql,postgresql}/` — per-dialect migrations.\n- `application/src/main/resources/extensions/` — extension schemas and role templates.\n- `application/src/test/` — backend tests.\n\n## Commands\n\n- `./gradlew :application:test` — run backend tests.\n- `./gradlew :application:bootRun` — start the backend dev server.\n- `./gradlew :application:copyUiDist` — embed the built UI into the JAR.\n- `./gradlew generateOpenApiDocs` — emit the OpenAPI spec used to regenerate the UI client.\n\n## Conventions\n\n- Keep reactive flows non-blocking; no blocking I/O in request paths.\n- Database migrations are versioned per dialect — never auto-generate them, and review them with the `application/` guide in mind.\n- Tests: JUnit 5 with Spring Boot test support; name unit tests `XxxTest` and integration tests `XxxIntegrationTest`.\n- After DTO, route, or schema changes, regenerate the UI client (`./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen`) and update `ui/` usage.\n\n## Boundaries\n\n- Ask before adding database migrations, changing security configuration, or introducing new dependencies.\n\n","application/CLAUDE.md":"@AGENTS.md\n","ui/AGENTS.md":"# Frontend Guidelines\n\n`ui/` is the Vue 3 + TypeScript + TailwindCSS frontend, managed with pnpm workspaces.\n\n## Structure\n\n- `ui/console-src/` — console application (entry: `console.html`, alias `@console`).\n- `ui/uc-src/` — user center application (entry: `uc.html`, alias `@uc`).\n- `ui/src/` — shared code: build-time Vite helpers (`src/vite/`), locales, assets, and shared views (alias `@`); not an application entry point.\n- `ui/packages/` — shared workspace packages (`@halo-dev/api-client`, `@halo-dev/components`, `@halo-dev/console-shared`, `@halo-dev/editor`, `@halo-dev/shared`, `@halo-dev/ui-plugin-bundler-kit`).\n- Unit tests: `*.spec.ts` files, usually under `__tests__/` next to the code they cover.\n\n## Commands\n\nRun from the repo root:\n\n- `pnpm -C ui install` / `pnpm -C ui dev` — install dependencies / run the dev server.\n- `pnpm -C ui build` — production build.\n- `pnpm -C ui typecheck` — `vue-tsc` type checking.\n- `pnpm -C ui lint` — ESLint (fails on any warning).\n- `pnpm -C ui format` / `pnpm -C ui format:check` — Prettier formatting.\n- `pnpm -C ui test:unit` — Vitest unit tests.\n- `./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen` — regenerate `ui/packages/api-client` after backend contract changes.\n\n## Conventions\n\n- Follow existing component and composable patterns; match surrounding code rather than introducing new stylistic islands.\n- Never hand-edit `ui/packages/api-client/src/` — it is generated; regenerate it instead.\n- Keep application dependency versions in `ui/package.json`. For dependencies shared by workspace packages, define versions in the catalog in `ui/pnpm-workspace.yaml` and reference them with `catalog:`; never duplicate version declarations across packages.\n- Payload or UX changes usually ship together with the backend change that defines the contract.\n\n## Boundaries\n\n- New npm dependencies require approval.\n","ui/CLAUDE.md":"@AGENTS.md\n","ui/packages/api-client/README.md":"# @halo-dev/api-client\n\nHalo 2.0 的 JavaScript API 客户端请求库。使用 [OpenAPI Generator](https://openapi-generator.tech/) 生成。\n\n## 使用\n\n```javascript\nimport {\n  coreApiClient,\n  consoleApiClient,\n  ucApiClient,\n  publicApiClient,\n  createCoreApiClient,\n  createConsoleApiClient,\n  createUcApiClient,\n  createPublicApiClient,\n  axiosInstance,\n} from \"@halo-dev/api-client\";\n```\n\n- **coreApiClient**: 为 Halo 所有自定义模型的 CRUD 接口封装的 api client。\n- **consoleApiClient**: 为 Halo 针对 Console 提供的接口封装的 api client。\n- **ucApiClient**: 为 Halo 针对 UC 提供的接口封装的 api client。\n- **publicApiClient**: 为 Halo 所有公开访问的接口封装的 api client。\n- **createCoreApiClient**: 用于创建自定义模型的 CRUD 接口封装的 api client，需要传入 axios 实例。\n- **createConsoleApiClient**: 用于创建 Console 接口封装的 api client，需要传入 axios 实例。\n- **createUcApiClient**: 用于创建 UC 接口封装的 api client，需要传入 axios 实例。\n- **createPublicApiClient**: 用于创建公开访问接口封装的 api client，需要传入 axios 实例。\n- **axiosInstance**: 内部默认创建的 axios 实例。\n\n### 在插件中使用\n\n```shell\npnpm install @halo-dev/api-client axios\n```\n\n由于已经在 Console 和 UC 项目中引入并设置好了 Axios 拦截器，所以直接使用即可：\n\n```javascript\nimport { coreApiClient } from \"@halo-dev/api-client\";\n\ncoreApiClient.content.post.listPost().then((response) => {\n  // handle response\n});\n```\n\n此外，在最新的 `@halo-dev/ui-plugin-bundler-kit@2.17.0` 中，已经排除了 `@halo-dev/api-client`、`axios` 依赖，所以最终产物中的相关依赖会自动使用 Halo 本身提供的依赖，无需关心最终产物大小。\n\n详细文档可查阅：[插件开发 / API 请求](https://docs.halo.run/developer-guide/plugin/api-reference/ui/api-request)\n\n### 在外部项目中使用\n\n```shell\npnpm install @halo-dev/api-client axios\n```\n\n```javascript\nimport axios from \"axios\";\n\nconst axiosInstance = axios.create({\n  baseURL: \"http://localhost:8090\",\n});\n\nconst coreApiClient = createCoreApiClient(axiosInstance);\n\ncoreApiClient.content.post.listPost().then((response) => {\n  // handle response\n});\n```\n","ui/packages/console-shared/README.md":"Please use `@halo-dev/ui-shared`, this package is only for compatibility.\n","ui/packages/editor/README.md":"# @halo-dev/richtext-editor\n\nThe default editor for Halo, built with [Tiptap](https://tiptap.dev/) + [ProseMirror](https://prosemirror.net/).\n\n> ⚠️ This package is not suitable for general editor use cases, as it is tightly coupled with Halo and uses some of Halo's built-in tools and shared dependencies.\n> However, you can use it in Halo plugins just like you would use it in Halo itself.\n\n## Installation\n\nIn Halo plugins:\n\n```bash\npnpm install @halo-dev/richtext-editor\n```\n\n## Usage\n\n```vue\n<script setup lang=\"ts\">\nimport {\n  ExtensionsKit,\n  RichTextEditor,\n  VueEditor,\n} from \"@halo-dev/richtext-editor\";\nimport { onMounted, ref, shallowRef } from \"vue\";\n\nconst editor = shallowRef<VueEditor>();\nconst content = ref(\"Hello World\");\n\nonMounted(() => {\n  editor.value = new VueEditor({\n    content: content.value,\n    extensions: [ExtensionsKit],\n    parseOptions: {\n      preserveWhitespace: true,\n    },\n    onUpdate: () => {\n      content.value = editor.value?.getHTML() || \"\";\n    },\n  });\n});\n</script>\n<template>\n  <RichTextEditor v-if=\"editor\" :editor=\"editor\" />\n</template>\n```\n\n## Requirements\n\n- Vue 3.5.x or higher\n- Halo plugin environment\n\n## Extension development\n\n- [Editor extensions, toolbar items, and keyboard shortcuts](./docs/extension.md)\n- [Editor extension runtime metadata](./docs/runtime-metadata.md)\n\n## Links\n\n- [Halo](https://github.com/halo-dev/halo)\n- [Documentation](https://docs.halo.run)\n- [Report Issues](https://github.com/halo-dev/halo/issues)\n\n## License\n\nGPL-3.0\n","ui/packages/editor/docs/extension.md":"# 扩展说明\n\n本文档介绍如何对编辑器的功能进行扩展，包括但不限于扩展工具栏、悬浮工具栏、Slash Command、拖拽功能等。各扩展区域参考下图：\n\n![编辑器扩展说明](extension.png)\n\n目前支持的所有扩展类型 [ExtensionOptions](../packages/editor/src/types/index.ts) 如下所示：\n\n```ts\nexport interface ExtensionOptions {\n  // 顶部工具栏扩展\n  getToolbarItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => ToolbarItemType | ToolbarItemType[];\n\n  // Slash Command 扩展\n  getCommandMenuItems?: () => CommandMenuItemType | CommandMenuItemType[];\n\n  // 悬浮菜单扩展\n  getBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenuType;\n\n  // 工具箱扩展\n  getToolboxItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => ToolboxItemType | ToolboxItemType[];\n\n  // 拖拽菜单扩展\n  getDraggableMenuItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => DragButtonType | DragButtonType[];\n}\n```\n\n> 对于 Tiptap 本身的扩展方式可以参考 <https://tiptap.dev/api/introduction>\n\n## 1. 顶部工具栏扩展\n\n编辑器顶部功能区域内容的扩展，通常用于增加用户常用操作，例如文本加粗、变更颜色等。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/16> 中，我们实现了对顶部工具栏的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolbarItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolbarItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getToolbarItems` 即为对顶部工具栏的扩展。其返回类型为：\n\n```ts\n// 顶部工具栏扩展\ngetToolbarItems?: ({\n  editor,\n}: {\n  editor: Editor;\n}) => ToolbarItemType | ToolbarItemType[];\n\n// 工具栏\nexport interface ToolbarItemType {\n  priority: number;\n  component: Component;\n  props: {\n    editor: Editor;\n    isActive: boolean;\n    disabled?: boolean;\n    icon?: Component;\n    title?: string;\n    shortcutId?: string;\n    shortcutIds?: string[];\n    action?: () => void;\n  };\n  children?: ToolbarItemType[];\n}\n```\n\n如下为 [`Bold`](../packages/editor/src/extensions/bold/index.ts) 扩展中对于 `getToolbarItems` 的扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n    getToolbarItems({ editor }: { editor: Editor }) {\n      return {\n        priority: 40,\n        component: markRaw(ToolbarItem),\n        props: {\n          editor,\n          isActive: editor.isActive(\"bold\"),\n          icon: markRaw(MdiFormatBold),\n          title: i18n.global.t(\"editor.common.bold\"),\n          action: () => editor.chain().focus().toggleBold().run(),\n        },\n      };\n    },\n  };\n},\n```\n\n## 2. 快捷键与提示信息\n\nHalo 在 Tiptap 的 `addKeyboardShortcuts` 基础上提供了快捷键描述注册表。第三方扩展仍然只需要实现一个 `addKeyboardShortcuts`，同时即可获得以下能力：\n\n- 执行 Tiptap 快捷键命令；\n- 在“键盘快捷键”侧边栏中展示操作说明；\n- 通过同一个 `shortcutId` 在工具栏或悬浮菜单的 tooltip 中展示快捷键；\n- 根据当前操作系统将 `Mod`、`Alt` 等按键格式化为对应的展示形式。\n\n### 2.1 注册快捷键并关联工具栏\n\n使用 `defineHaloKeyboardShortcuts` 定义快捷键，再将相同的 `id` 传给工具栏组件的 `shortcutId`：\n\n```ts\nimport {\n  defineHaloKeyboardShortcuts,\n  Extension,\n  ToolbarItem,\n  type Editor,\n  type ExtensionOptions,\n} from \"@halo-dev/richtext-editor\";\nimport { markRaw } from \"vue\";\nimport MyIcon from \"./MyIcon.vue\";\n\nconst shortcutId = \"plugin.example.insertGreeting\";\n\nfunction insertGreeting(editor: Editor) {\n  return editor.chain().focus().insertContent(\"Hello Halo\").run();\n}\n\nexport const ExtensionExample = Extension.create<ExtensionOptions>({\n  name: \"exampleShortcut\",\n\n  addKeyboardShortcuts() {\n    return defineHaloKeyboardShortcuts(this, [\n      {\n        id: shortcutId,\n        keys: [\"Mod-Alt-g\"],\n        label: \"插入问候语\",\n        category: \"general\",\n        priority: 100,\n        command: () => insertGreeting(this.editor),\n      },\n    ]);\n  },\n\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolbarItems({ editor }: { editor: Editor }) {\n        return {\n          priority: 100,\n          component: markRaw(ToolbarItem),\n          props: {\n            editor,\n            isActive: false,\n            icon: markRaw(MyIcon),\n            title: \"插入问候语\",\n            shortcutId,\n            action: () => insertGreeting(editor),\n          },\n        };\n      },\n    };\n  },\n});\n```\n\n`ToolbarItem`、`ToolbarSubItem` 和 `BubbleItem` 都支持 `shortcutId`。`ToolbarItem` 还支持 `shortcutIds`，适用于一个按钮对应多个操作的情况，例如同时展示“增大字号”和“减小字号”。tooltip 会展示每个快捷键定义中的第一组按键，快捷键侧边栏则会展示 `keys` 中的全部可选按键。\n\n默认编辑器已经通过 `ExtensionsKit` 内置 `ExtensionKeyboardShortcuts`，插件通过 `default:editor:extension:create` 扩展点注册时无需重复添加。自行创建编辑器实例时，应使用 `ExtensionsKit`，或显式加入 `ExtensionKeyboardShortcuts`。\n\n### 2.2 描述字段\n\n`defineHaloKeyboardShortcuts` 接收的每一项都是一个 `HaloKeyboardShortcutDefinition`：\n\n| 字段 | 必填 | 说明 |\n| --- | --- | --- |\n| `id` | 是 | 编辑器内稳定且唯一的标识符，用于关联 tooltip。插件应使用包含插件标识的命名空间，例如 `plugin.example.insertGreeting`。 |\n| `keys` | 是 | Tiptap 格式的按键组合。第一项是 tooltip 展示的主快捷键，全部按键都会展示在快捷键侧边栏中。 |\n| `label` | 是 | 用户可见的操作名称，可以是字符串或返回字符串的函数。 |\n| `category` | 是 | 快捷键侧边栏分组：`general`、`formatting`、`structure` 或 `navigation`。 |\n| `command` | 视情况 | 新增快捷键时必须提供。扩展已有 Tiptap 快捷键时可以省略，此时复用父扩展中相同按键的命令。 |\n| `description` | 否 | 操作的补充说明，可以是字符串或返回字符串的函数。 |\n| `priority` | 否 | 在快捷键侧边栏同一分组中的排序值，数值越小越靠前，默认为 `100`。 |\n| `discoverable` | 否 | 是否出现在快捷键侧边栏中，默认为 `true`。即使设为 `false`，显式绑定了 `shortcutId` 的 tooltip 仍可展示。 |\n| `visible` | 否 | 根据当前编辑器状态决定是否出现在快捷键侧边栏中。 |\n\n按键名称遵循 [Tiptap 快捷键格式](https://tiptap.dev/docs/editor/core-concepts/keyboard-shortcuts)。建议使用 `Mod` 表示 macOS 的 `Command` 和 Windows/Linux 的 `Control`，例如 `Mod-b`。命令处理成功时应返回 `true`，这样 ProseMirror 会阻止浏览器继续执行同一按键的默认行为；未处理时应返回 `false`。\n\n如果扩展继承的 Tiptap 扩展已经实现了相同按键，可以只补充 Halo 的描述信息，不需要重新实现命令：\n\n```ts\naddKeyboardShortcuts() {\n  return defineHaloKeyboardShortcuts(this, [\n    {\n      id: \"plugin.example.toggleFeature\",\n      keys: [\"Mod-b\"],\n      label: \"切换示例功能\",\n      category: \"formatting\",\n    },\n  ]);\n},\n```\n\n只有父扩展确实定义了 `keys` 中对应的按键时才能省略 `command`。开发环境会对缺少实际命令的定义输出警告，并且不会注册这条描述。\n\n### 2.3 自定义组件中的 tooltip\n\n完全自定义工具栏组件时，可以通过 `useHaloKeyboardShortcut` 响应式读取注册表，再使用 `KeyboardShortcutTooltip` 保持与内置工具栏一致的视觉和无障碍信息：\n\n```vue\n<script setup lang=\"ts\">\nimport {\n  KeyboardShortcutTooltip,\n  useHaloKeyboardShortcut,\n  type Editor,\n} from \"@halo-dev/richtext-editor\";\n\nconst props = defineProps<{\n  editor: Editor;\n  shortcutId: string;\n  title: string;\n}>();\n\nconst shortcut = useHaloKeyboardShortcut(props.editor, () => props.shortcutId);\n</script>\n\n<template>\n  <KeyboardShortcutTooltip\n    v-slot=\"tooltipProps\"\n    :title=\"title\"\n    :shortcut=\"shortcut?.keys[0]\"\n  >\n    <button :aria-label=\"tooltipProps.ariaLabel\" type=\"button\">\n      {{ title }}\n    </button>\n  </KeyboardShortcutTooltip>\n</template>\n```\n\n一个组件需要读取多条快捷键时，可以使用 `useHaloKeyboardShortcuts(editor, () => shortcutIds)`。这两个 composable 必须在 Vue 组件的 `setup` 阶段调用，以便组件卸载时自动取消注册表订阅。\n\n### 2.4 命名与冲突规则\n\n- `id` 应包含插件标识，避免覆盖其他扩展注册的描述；快捷键注册表不会自动为重复 ID 添加命名空间。\n- 只注册产品中真实可执行的快捷键，不要为了填满快捷键侧边栏而自行创造按键组合。\n- 添加按键前应检查 Halo 默认快捷键、Tiptap 默认快捷键以及浏览器常用快捷键。确实需要覆盖浏览器默认行为时，命令必须在成功处理后返回 `true`。\n- `label` 和 `description` 应面向用户描述操作，不要使用内部命令名或扩展名。\n\n## 3. 工具箱扩展\n\n编辑器工具箱区域的扩展，可用于增加编辑器附属操作，例如插入表格，插入第三方组件等功能。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/27> 中，我们实现了对编辑器工具箱区域的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolboxItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolboxItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getToolboxItems` 即为对工具箱的扩展。其返回类型为：\n\n```ts\n// 工具箱扩展\ngetToolboxItems?: ({\n  editor,\n}: {\n  editor: Editor;\n}) => ToolboxItemType | ToolboxItemType[];\n\nexport interface ToolboxItemType {\n  priority: number;\n  component: Component;\n  props: {\n    editor: Editor;\n    icon?: Component;\n    title?: string;\n    description?: string;\n    action?: () => void;\n  };\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getToolboxItems` 工具箱的扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n    getToolboxItems({ editor }: { editor: Editor }) {\n      return {\n        priority: 15,\n        component: markRaw(ToolboxItem),\n        props: {\n          editor,\n          icon: markRaw(MdiTablePlus),\n          title: i18n.global.t(\"editor.menus.table.add\"),\n          action: () =>\n            editor\n              .chain()\n              .focus()\n              .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n              .run(),\n        },\n      };\n    },\n  }\n}\n```\n\n## 4. Slash Command 扩展\n\nSlash Command （斜杠命令）的扩展，可用于在当前行快捷执行功能操作，例如转换当前行为标题、在当前行添加代码块等功能。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/16> 中，我们实现了对 Slash Command 指令的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getCommandMenuItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getCommandMenuItems() {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getCommandMenuItems` 即为对工具箱的扩展。其返回类型为：\n\n```ts\n// Slash Command 扩展\ngetCommandMenuItems?: () => CommandMenuItemType | CommandMenuItemType[];\n\nexport interface CommandMenuItemType {\n  priority: number;\n  icon: Component;\n  title: string;\n  keywords: string[];\n  command: ({ editor, range }: { editor: Editor; range: Range }) => void;\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getCommandMenuItems` 的扩展示例：\n\n```ts\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getCommandMenuItems() {\n        return {\n          priority: 120,\n          icon: markRaw(MdiTable),\n          title: \"editor.extensions.commands_menu.table\",\n          keywords: [\"table\", \"biaoge\"],\n          command: ({ editor, range }: { editor: Editor; range: Range }) => {\n            editor\n              .chain()\n              .focus()\n              .deleteRange(range)\n              .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n              .run();\n          },\n        };\n      },\n    }\n  }\n```\n\n## 5. 悬浮菜单扩展\n\n编辑器悬浮菜单的扩展。可用于支持目标元素组件的功能扩展及操作简化。例如 `Table` 扩展中的添加下一列、添加上一列等操作。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/38> 中，我们重构了对编辑器悬浮区域的扩展，如果需要对某个块进行支持，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getBubbleMenu` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getBubbleMenu({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getBubbleMenu` 即为对悬浮菜单的扩展。其返回类型为：\n\n```ts\n// 悬浮菜单扩展\ngetBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenuType;\n\ninterface BubbleMenuProps {\n  pluginKey?: string;                                             // 悬浮菜单插件 Key，建议命名方式 xxxBubbleMenu\n  editor?: Editor;\n  shouldShow: (props: {                                           // 悬浮菜单显示的条件\n    editor: Editor;\n    state: EditorState;\n    node?: HTMLElement;\n    view?: EditorView;\n    oldState?: EditorState;\n    from?: number;\n    to?: number;\n  }) => boolean;\n  tippyOptions?: Record<string, unknown>;                          // 可自由定制悬浮菜单所用的 tippy 组件的选项\n  getRenderContainer?: (node: HTMLElement) => HTMLElement;         // 悬浮菜单所基准的 DOM\n  defaultAnimation?: boolean;                                      // 是否启用默认动画。默认为 true\n}\n\n// 悬浮菜单\nexport interface NodeBubbleMenuType extends BubbleMenuProps {\n  component?: Component;                                           // 不使用默认的样式，与 items 二选一\n  items?: BubbleItemType[];                                       // 悬浮菜单子项，使用默认的形式进行，与 items 二选一\n  extendsKey?: string | PluginKey;                                 // 用于扩展已有悬浮菜单的 key，如果未提供，则会被视为一个新的悬浮菜单\n}\n\n// 悬浮菜单子项\nexport interface BubbleItemType {\n  priority: number;                                                // 优先级，数字越小优先级越大，越靠前\n  component?: Component;                                           // 完全自定义子项样式\n  key?: string;                                                    // 子项的唯一标识，通常用于扩展悬浮菜单时仅保留唯一的子项。\n  props?: {                                                        // 子项属性，可选。同时支持传入自定义属性\n    isActive?: ({ editor }: { editor: Editor }) => boolean;         // 当前功能是否已经处于活动状态\n    visible?: ({ editor }: { editor: Editor }) => boolean;         // 是否显示当前子项\n    icon?: Component;                                              // 图标\n    iconStyle?: string;                                            // 图标自定义样式\n    title?: string;                                                // 标题\n    action?: ({ editor }: { editor: Editor }) => Component | void; // 点击子项后的操作，如果返回 Component，则会将其包含在下拉框中。\n  } & Record<string, unknown>;\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getBubbleMenu` 悬浮菜单的部分扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n      getBubbleMenu({ editor }) {\n        return {\n          pluginKey: \"tableBubbleMenu\",\n          shouldShow: ({ state }: { state: EditorState }): boolean => {\n            return isActive(state, Table.name);\n          },\n          getRenderContainer(node) {\n            let container = node;\n            if (container.nodeName === \"#text\") {\n              container = node.parentElement as HTMLElement;\n            }\n            while (\n              container &&\n              container.classList &&\n              !container.classList.contains(\"tableWrapper\")\n            ) {\n              container = container.parentElement as HTMLElement;\n            }\n            return container;\n          },\n          tippyOptions: {\n            offset: [26, 0],\n          },\n          items: [\n            {\n              priority: 10,\n              props: {\n                icon: markRaw(MdiTableColumnPlusBefore),\n                title: i18n.global.t(\"editor.menus.table.add_column_before\"),\n                action: () => editor.chain().focus().addColumnBefore().run(),\n              },\n            },\n          ]\n        }\n      }\n  }\n}\n```\n\n## 6. 拖拽菜单扩展\n\n拖拽菜单扩展主要用于拖拽的菜单功能扩展，例如转换为、复制、剪切、删除等操作。\n\n在 <https://github.com/halo-dev/halo/pull/7861> 中，我们重构了对编辑器拖拽区域的扩展，并且支持了对拖拽菜单的扩展。如果需要对拖拽菜单进行扩展，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getDraggableMenuItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n同时，为了支持不同扩展对同一菜单项的扩展，我们提供了 `extendsKey` 属性，用于指定扩展目标菜单项的唯一标识。只需将 `extendsKey` 设置为已有的菜单项的 `key`，即可扩展该菜单项。可扩展已有菜单项的 `visible`、`isActive`、`disabled`、`action` 方法以及 `children.items` 属性，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return {\n          extendsKey: CONVERT_TO_KEY,\n          // 当任意扩展目标菜单项的 visible 方法返回 false 时，当前菜单项不会显示。返回 true 则会继续执行后续的扩展实现。\n          visible: ({ editor }) => {\n            if (isActive(editor.state, \"table\")) {\n              return false;\n            }\n            return true;\n          },\n        };\n      },\n    };\n  },\n};\n```\n\n拖拽菜单最多支持两级菜单嵌套， 如果想扩展已有的一级菜单，为其二级菜单增加内容，则需要同时设置 `extendsKey` 和 `children.items` 属性。如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return {\n          extendsKey: CONVERT_TO_KEY,\n          children: {\n            items: [\n              {\n                priority: 10,\n                icon: markRaw(MdiFormatParagraph),\n                title: i18n.global.t(\"editor.common.heading.paragraph\"),\n                action: ({ editor }: { editor: Editor }) =>\n                  editor.chain().focus().setParagraph().run(),\n              },\n            ],\n          },\n        }\n      },\n    };\n  },\n}\n```\n\n默认情况下，将会追加 `items`，若想覆盖，则需要设置子菜单的 `key` 属性，将会覆盖原有的子菜单项。\n\n下面为 `getDraggableMenuItems` 的返回类型：\n\n```ts\n\n// 拖拽菜单扩展\ngetDraggableMenuItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => DragButtonType | DragButtonType[];\n\n// 拖拽菜单项目属性\nexport interface DragButtonItemProps {\n  extendsKey?: string;                                    // 扩展目标菜单项的唯一标识，如果提供了该属性，则视为扩展目标菜单项。\n  key?: string;                                           // 唯一标识，如果同级菜单项设置了同样的 key，则会被合并为一个菜单项。\n  priority?: number;                                      // 优先级，数字越小优先级越大，越靠前\n  title?: string | (() => string);                        // 标题\n  icon?: Component;                                       // 图标\n  action?: ({                                             // 点击菜单后的操作，如果返回 Component，则会将其包含在子菜单中。\n                                                          // 可以通过调用 close 方法可以在操作完成后关闭拖拽菜单，或者当返回为 true 或 undefined 时，会自动关闭拖拽菜单，如果返回 false，则不会关闭拖拽菜单。\n                                                          // 多个扩展实现时，则按照顺序执行，并在返回非 undefined 值时停止执行。\n    editor,\n    node,\n    pos,\n    close,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n    close: () => void;\n  }) => Component | boolean | void | Promise<Component | boolean | void>;\n  iconStyle?: string;                                       // 图标自定义样式\n  class?: string;                                           // 自定义样式\n  visible?: ({                                              // 是否显示当前菜单项，默认为 true，多个扩展实现时，以 AND 逻辑判断，即所有扩展返回 true 时，当前菜单项才会显示。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  isActive?: ({                                             // 当前菜单项是否处于活动状态，默认为 false，多个扩展实现时，以 OR 逻辑判断，即只要有一个扩展返回 true，则当前菜单项处于活动状态。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  disabled?: ({                                                // 是否禁用当前菜单项，默认为 false，多个扩展实现时，以 OR 逻辑判断，即只要有一个扩展返回 true，则当前菜单项会被禁用。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  keyboard?: string;                                            // 快捷键，遵循 https://tiptap.dev/docs/editor/core-concepts/keyboard-shortcuts\n  component?: Component;                                        // 自定义组件，如果提供了该属性，则不会显示默认的菜单项，而是会显示自定义组件，并且将所有 props 传递给自定义组件。\n  [key: string]: any;                                           // 其他自定义属性，将会传递给自定义组件。\n}\n\n// 一级菜单项\nexport interface DragButtonType extends DragButtonItemProps {\n  children?: {                                                    // 子菜单项，如果提供了该属性，则视为扩展目标菜单项的二级菜单。\n    component?: Component;                                        // 自定义组件，如果提供了该属性，则不会显示默认的子菜单项，而是会显示自定义组件，并且将所有 props 传递给自定义组件。\n    items?: DragButtonItemProps[];                                // 子菜单项列表，如果提供了该属性，则视为扩展目标菜单项的二级菜单。\n  };\n}\n```\n\n## 7. 块缩进扩展\n\n编辑器会根据节点的 schema 元数据发现可缩进节点，不维护组件名称白名单。第三方节点只要属于 `block` group，且不属于 `list` group，就会自动获得块缩进属性、快捷键和拖拽缩进能力：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MyBlock = Node.create({\n  name: \"myBlock\",\n  group: \"block\",\n  // ...\n});\n```\n\n特殊节点可以通过 `haloEditorIndentation` 调整接入行为：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MyBlock = Node.create({\n  name: \"myBlock\",\n  group: \"block\",\n\n  // 光标在节点内部时，将 Tab / Shift-Tab 交给节点自身处理；\n  // 节点选中或光标位于节点左上角间隙时，仍可缩进整个节点。\n  haloEditorIndentation: {\n    keyboard: \"passthrough\",\n  },\n});\n```\n\n- 设置为 `false` 可以让 block 节点退出通用缩进。\n- 设置为 `true` 可以让不属于 block group 的特殊节点显式接入缩进。\n- `legacyLineIndent: true` 仅用于需要兼容旧版首行缩进数据的文本节点，不建议新扩展启用。\n\n自定义列表容器应加入 `list` group，列表项应加入 `listItem` group。编辑器通过这两个 schema group 识别列表层级、继承缩进和行内拖拽目标，不依赖 `bulletList`、`orderedList`、`listItem` 等具体节点名称。\n\n第三方块命令可以复用以下公共 helpers：\n\n```ts\nimport {\n  findAncestorListItems,\n  getBlockIndentAtSelection,\n  prepareBlockCommandFromList,\n} from \"@halo-dev/richtext-editor\";\n\nconst listItems = findAncestorListItems(editor.state.selection.$from);\nconst indent = getBlockIndentAtSelection(editor);\nconst preparedRange = prepareBlockCommandFromList(editor, range);\n```\n\n- `findAncestorListItems` 按 schema group 查找当前光标所在的列表项，结果从内层到外层排列。\n- `getBlockIndentAtSelection` 将显式块缩进和列表层级换算为当前配置下的可视缩进。\n- `prepareBlockCommandFromList` 适用于 Slash Command 一类块命令：移除触发文本、退出列表并保留可视缩进，且整个操作可以一次撤销。\n\n缩进步长、最小值、最大值和默认值都可以通过 `ExtensionsKit` 配置：\n\n```ts\nimport { ExtensionsKit } from \"@halo-dev/richtext-editor\";\n\nExtensionsKit.configure({\n  indent: {\n    indentRange: 32,\n    minIndentLevel: 0,\n    maxIndentLevel: 320,\n    defaultIndentLevel: 0,\n  },\n});\n```\n\n未配置 `maxIndentLevel` 时，默认允许 10 级缩进，并会随 `indentRange` 自动换算最大值。\n","ui/packages/editor/docs/runtime-metadata.md":"# 编辑器扩展的运行期元数据\n\n`@halo-dev/richtext-editor` 允许 Tiptap 的 Node、Mark 和 Extension\n声明运行期组件元数据，用于描述最终 Editor 实例中的 schema、组件用法、结构关系、\n属性和示例。这些信息从 Editor 当前注册的扩展及最终 schema 中同步解析；AI Agent\n是目前的主要消费者，但元数据本身是对运行期组件能力的描述，不会改变或约束组件行为。\n\n## 声明新组件\n\n下面的数学公式节点说明了适用场景、属性和生成所需的外部能力：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MathBlock = Node.create({\n  name: \"mathBlock\",\n  group: \"block\",\n  atom: true,\n\n  addAttributes() {\n    return {\n      formula: {\n        default: \"\",\n      },\n    };\n  },\n\n  parseHTML() {\n    return [{ tag: 'div[data-type=\"math-block\"]' }];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    return [\"div\", { ...HTMLAttributes, \"data-type\": \"math-block\" }];\n  },\n\n  // 声明运行期元数据中的 AI 使用说明\n  addHaloEditorMetadata() {\n    return {\n      ai: {\n        description: \"A display mathematical formula.\",\n        exposure: \"available\",\n        useWhen: [\"Presenting a standalone mathematical expression.\"],\n        attributeGuidance: {\n          formula: {\n            description: \"Formula source written in LaTeX.\",\n            format: \"LaTeX\",\n            examples: [\"E = mc^2\"],\n          },\n        },\n        generation: {\n          mode: \"requires-capability\",\n          requiredCapabilities: [\"math-to-html\"],\n        },\n        examples: [\n          '<div data-type=\"math-block\" formula=\"E = mc^2\"></div>',\n        ],\n      },\n    };\n  },\n});\n```\n\nCapability 名称是开放字符串。Halo 只把它放入 Manifest，不负责查找或执行对应工具，由最终使用者扩展工具。\n\n## 扩展现有组件\n\n元数据与 Tiptap 的 `.extend()` 继承链一起自动合并。插件只需返回自己的局部补丁，\n不需要调用 `this.parent`，也不需要手动合并 Halo 已有说明：\n\n```ts\nimport { ExtensionCodeBlock } from \"@halo-dev/richtext-editor\";\n\nexport const HighlightedCodeBlock = ExtensionCodeBlock.extend({\n  addAttributes() {\n    return {\n      ...this.parent?.(),\n      highlightTheme: {\n        default: null,\n      },\n    };\n  },\n\n  addHaloEditorMetadata() {\n    return {\n      ai: {\n        attributeGuidance: {\n          highlightTheme: {\n            description: \"Syntax-highlighting theme.\",\n            allowedValues: [\"github-light\", \"github-dark\"],\n            omitWhen: [\"The editor default theme should be used.\"],\n          },\n        },\n      },\n    };\n  },\n});\n```\n\n最终 Manifest 会同时包含原 code block 的描述和 `highlightTheme`。\n\n## 为全局属性贡献说明\n\nPlain Extension 不会成为 Manifest 组件，但可向明确命名的 Node 或 Mark\n贡献元数据。这适合 `addGlobalAttributes()`：\n\n```ts\nimport { Extension } from \"@halo-dev/richtext-editor\";\n\nexport const Tone = Extension.create({\n  name: \"tone\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"paragraph\", \"heading\"],\n        attributes: {\n          tone: {\n            default: null,\n          },\n        },\n      },\n    ];\n  },\n\n  addHaloEditorMetadata() {\n    return {\n      contributions: [\n        {\n          targets: [\n            { kind: \"node\", name: \"paragraph\" },\n            { kind: \"node\", name: \"heading\" },\n          ],\n          metadata: {\n            ai: {\n              attributeGuidance: {\n                tone: {\n                  description: \"Writing tone for this block.\",\n                  allowedValues: [\"neutral\", \"friendly\", \"formal\"],\n                },\n              },\n            },\n          },\n        },\n      ],\n    };\n  },\n});\n```\n\n贡献只应用于最终 schema 中存在的目标。多个贡献冲突时，根据组件定义中的 priority 决定，\n较高者优先；priority 相同时，后注册者优先。\n\n## 组件自身的结构说明\n\n组件只声明自己的父级和数量关系，不跨组件声明子节点：\n\n```ts\naddHaloEditorMetadata() {\n  return {\n    ai: {\n      description: \"An optional caption belonging to a figure.\",\n    },\n    structure: {\n      allowedParents: [\"figure\"],\n      minPerParent: 0,\n      maxPerParent: 1,\n    },\n  };\n}\n```\n\n这表示当前组件只能位于 `figure` 下，在每个 `figure` 中可省略且最多出现一次。\n\n## 禁用与兼容\n\n- `ai: false` 明确建议 AI 不主动使用该组件，但组件的 schema 信息仍会出现在\n  Manifest 中供 AI 理解。\n- 声明抛错、字段无效、属性或父节点不存在时，生成器保留其他有效数据；\n  开发环境给出警告，生产环境不会因元数据阻止编辑器运行。\n- 未知字段会被丢弃。请勿把 system prompt、可执行回调或敏感信息放入元数据。\n\n每段文本最多 1,000 个字符；说明数组和 aliases 最多 10 项；\n`allowedValues` 和属性示例最多 32 项；组件 HTML 示例最多 3 个且每个不超过\n4 KiB；单组件 AI 元数据最多 16 KiB；单个 Manifest 的 AI 元数据最多\n128 KiB。\n\n## 读取运行期 Manifest\n\n在 Editor 创建完成后同步生成最终快照：\n\n```ts\nimport {\n  createHaloEditorManifest,\n  type HaloEditorManifest,\n  type VueEditor,\n} from \"@halo-dev/richtext-editor\";\n\nfunction editorManifest(editor: VueEditor): HaloEditorManifest {\n  return createHaloEditorManifest(editor);\n}\n```\n\nManifest 是当前 Editor 实例的运行期快照，包含全部 Node 和 Mark、规范化元数据、\n`version: 1` 以及稳定的 `signature`。AI 插件可以把它加入模型上下文，其他消费者\n也可以用它了解当前编辑器实际注册的组件；消费者自行决定是否根据其中的建议进行额外校验。\n","ui/packages/ui-plugin-bundler-kit/README.md":"# @halo-dev/ui-plugin-bundler-kit\n\nA frontend build toolkit for Halo UI plugin development, supporting both Vite and Rsbuild build systems.\n\n## Introduction\n\n`@halo-dev/ui-plugin-bundler-kit` is a frontend build configuration toolkit specifically designed for Halo UI plugin development. It provides pre-configured build settings to help developers quickly set up and build frontend interfaces for Halo plugins and theme-provided UI plugins.\n\n### Key Features\n\n- 🚀 **Ready to Use** - Provides pre-configured Vite and Rsbuild build settings\n- 📦 **Multi-Build Tool Support** - Supports both Vite and Rsbuild\n- 🔧 **Flexible Configuration** - Supports custom build configurations\n- 🎯 **Halo Optimized** - External dependencies and global variables optimized for Halo UI plugin development\n- 📁 **Smart Output** - Automatically selects output directory based on environment\n\n## Installation\n\n```bash\n# Using npm\nnpm install @halo-dev/ui-plugin-bundler-kit\n\n# Using yarn\nyarn add @halo-dev/ui-plugin-bundler-kit\n\n# Using pnpm\npnpm add @halo-dev/ui-plugin-bundler-kit\n```\n\n### Additional Dependencies\n\n**For Vite users**, install Vite and its Vue plugin:\n\n```bash\nnpm install vite @vitejs/plugin-vue\n```\n\n**For Rsbuild users**, install Rsbuild and its Vue plugin:\n\n```bash\nnpm install @rsbuild/core @rsbuild/plugin-vue\n```\n\nThe Vite and Rsbuild peer dependencies are optional. Install only the build tool and Vue plugin you use, and import its configuration helper from `@halo-dev/ui-plugin-bundler-kit/vite` or `@halo-dev/ui-plugin-bundler-kit/rsbuild`. The package root exports and the deprecated `HaloUIPluginBundlerKit` helper have been removed.\n\n## Usage\n\n### Vite Configuration\n\nCreate or update `vite.config.ts` file in your UI plugin project root:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  // provider defaults to \"plugin\"\n  vite: {\n    // Your custom Vite configuration\n    plugins: [\n      // Additional plugins (Vue plugin is already included)\n    ],\n    // Other configurations...\n  },\n});\n```\n\n> **Note**: Vue plugin is pre-configured. Pass its options through the top-level `vue` field instead of adding another `@vitejs/plugin-vue` instance.\n\n### Rsbuild Configuration\n\nCreate or update `rsbuild.config.ts` file in your UI plugin project root:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  // provider defaults to \"plugin\"\n  rsbuild: {\n    // Your custom Rsbuild configuration\n    plugins: [\n      // Additional plugins (Vue plugin is already included)\n    ],\n    // Other configurations...\n  },\n});\n```\n\n> **Note**: Vue plugin is pre-configured. Pass its options through the top-level `vue` field instead of adding another `@rsbuild/plugin-vue` instance.\n\n### Theme UI Plugin Configuration\n\nFor theme-provided Console/User Center UI plugins, place the frontend project under the theme's `ui-plugin/` directory:\n\n```text\ntheme-root/\n├── theme.yaml\n└── ui-plugin/\n    ├── package.json\n    ├── src/index.ts\n    └── vite.config.ts\n```\n\nVite:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  provider: \"theme\",\n  vite: {},\n});\n```\n\nRsbuild:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  provider: \"theme\",\n  rsbuild: {},\n});\n```\n\nThe theme provider reads `../theme.yaml`, outputs to `dist`, registers the module as `theme:{metadata.name}`, and configures assets for `/themes/{metadata.name}/ui-plugin/assets/`. Halo reads only `ui-plugin/dist/**` from the theme package.\n\n### Output Format and Halo Target\n\n`viteConfig` and `rsbuildConfig` accept the same format options:\n\n```typescript\nexport default viteConfig({\n  format: \"auto\", // \"auto\" | \"iife\" | \"esm\"\n  vite: {},\n});\n```\n\n`auto` is the default. It emits ESM when `spec.requires` is a stable Halo version or a simple `>=MAJOR.MINOR.PATCH` target whose minimum is Halo 2.26.0 or newer. Missing, wildcard, composite, or otherwise unsupported ranges produce a warning and keep the compatible IIFE output. This fallback is intentional: ESM is optional, and Halo continues loading old IIFE plugin and theme artifacts throughout Halo 2.x.\n\nUse an explicit target only when intentionally forcing ESM and a target cannot be derived:\n\n```typescript\nexport default viteConfig({\n  format: \"esm\",\n  targetHaloVersion: \"2.26.0\",\n  vite: {},\n});\n```\n\nAfter ESM is selected, successful builds generate `ui-plugin.json`. This filename is reserved for the bundler kit: do not create, copy, or emit another file with that name. A provider artifact without this file remains legacy, even if its `spec.requires` also supports Halo 2.26 or newer.\n\nESM entries must default-export the existing `PluginModule`. The generated manifest records one optional main stylesheet; CSS belonging to asynchronous chunks stays out of the manifest and loads on demand with its JavaScript chunk. Halo starts every provider-owned startup stylesheet and entry in parallel, then commits modules in provider order without reloading after each entry. Top-level module effects, timers, listeners, and arbitrary asynchronous effects are not transactional; a full page reload remains the lifecycle and recovery boundary after provider changes.\n\nThe default Vite and Rsbuild presets provide ESM output, provider-relative resource paths, content-hashed entry and startup-style filenames, content-hashed secondary resources, and Halo shared-runtime externals. The generated manifest records the actual startup filenames. Raw `vite` and `rsbuild` configuration is merged after these defaults and is not inspected, rejected, or rewritten. If it changes entry names, formats, public paths, externals, or caching behavior, the caller owns the resulting manifest consistency, Import Map compatibility, shared dependency identity, resource relocation, and cache safety. Compatible IIFE overrides retain their previous stable `main.js` behavior.\n\n### Shared Runtime Dependencies\n\nESM providers may import these package roots from Halo:\n\n- `vue`\n- `vue-router`\n- `pinia`\n- `axios`\n- `@formkit/vue`\n- `@formkit/core`\n- `@halo-dev/ui-shared`\n- `@halo-dev/components`\n- `@halo-dev/api-client`\n- `@halo-dev/richtext-editor`\n\nThe bundler discovers shared package root imports and reports the installed provider version beside the selected Halo host snapshot version when package metadata is available. A newer provider dependency emits a compatibility note, and a different major emits a stronger note, but version drift does not fail the build. Export usage, aliases, forks, and final bundler resolution are not inspected. Deep imports such as `vue/dist/vue.esm-bundler.js` still fail because Halo's Import Map exposes shared package roots only.\n\nVue, Vue Router, Pinia, and the FormKit Vue/Core graph share host identity. Other `@formkit/*` packages and VueUse stay provider-private. Non-shared dependencies are bundled by the default presets.\n\nThe shared `axios` import is the standard package module. Do not mutate its shared defaults or interceptors. For isolated clients, call `axios.create()`. `@halo-dev/api-client` exports Halo's separate authenticated `axiosInstance`; do not mutate that instance either.\n\n### Querying Other UI Providers\n\nUse the shared registration store instead of checking another provider's `window.PluginName` global:\n\n```typescript\nimport { stores } from \"@halo-dev/ui-shared\";\n\nconst uiPlugins = stores.uiPlugins();\n\nuiPlugins.isEnabled(\"plugin-search\");\nuiPlugins.isRegistered(\"plugin-search\");\nuiPlugins.get(\"plugin-search\");\n```\n\nThe reactive record contains only Halo-owned `name`, `type`, `version`, and `pending | registered | failed` status. `isEnabled` means the provider was discovered in the current descriptor; `isRegistered` becomes true after its current-page registration succeeds. Provider code treats this store as read-only and must not depend on another provider's evaluation order or module object.\n\n## Configuration Options\n\n### Vite Configuration Options\n\n```typescript\ninterface ViteUserConfig {\n  /**\n   * UI plugin provider type\n   * @default \"plugin\"\n   */\n  provider?: \"plugin\" | \"theme\";\n\n  /**\n   * Halo plugin or theme manifest file path\n   * @default \"../src/main/resources/plugin.yaml\" for plugins, \"../theme.yaml\" for themes\n   */\n  manifestPath?: string;\n\n  /** @default \"auto\" */\n  format?: \"auto\" | \"iife\" | \"esm\";\n\n  /** Required for explicit ESM when spec.requires has no derivable target. */\n  targetHaloVersion?: string;\n\n  /** Options for the built-in @vitejs/plugin-vue instance. */\n  vue?: VuePluginOptions;\n\n  /**\n   * Custom Vite configuration\n   */\n  vite: UserConfig | UserConfigFnObject;\n}\n```\n\n### Rsbuild Configuration Options\n\n```typescript\ninterface RsBuildUserConfig {\n  /**\n   * UI plugin provider type\n   * @default \"plugin\"\n   */\n  provider?: \"plugin\" | \"theme\";\n\n  /**\n   * Halo plugin or theme manifest file path\n   * @default \"../src/main/resources/plugin.yaml\" for plugins, \"../theme.yaml\" for themes\n   */\n  manifestPath?: string;\n\n  /** @default \"auto\" */\n  format?: \"auto\" | \"iife\" | \"esm\";\n\n  /** Required for explicit ESM when spec.requires has no derivable target. */\n  targetHaloVersion?: string;\n\n  /** Options for the built-in @rsbuild/plugin-vue instance. */\n  vue?: PluginVueOptions;\n\n  /**\n   * Custom Rsbuild configuration\n   */\n  rsbuild: RsbuildConfig | ((env: ConfigParams) => RsbuildConfig);\n}\n```\n\n## Advanced Configuration Examples\n\n### Customizing the Vue Compiler\n\nVite:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  vue: {\n    template: {\n      compilerOptions: {\n        isCustomElement: (tag) => tag === \"halo-app-card\",\n      },\n    },\n  },\n  vite: {},\n});\n```\n\nRsbuild:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  vue: {\n    vueLoaderOptions: {\n      compilerOptions: {\n        isCustomElement: (tag) => tag === \"halo-app-card\",\n      },\n    },\n  },\n  rsbuild: {},\n});\n```\n\nThe helper owns the Vue plugin instance. Keep `@vitejs/plugin-vue` and `@rsbuild/plugin-vue` out of the nested `plugins` array to avoid running the SFC transform twice.\n\n### Adding Path Aliases (Vite)\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\nimport path from \"path\";\n\nexport default viteConfig({\n  vite: {\n    resolve: {\n      alias: {\n        \"@\": path.resolve(__dirname, \"src\"),\n        \"@components\": path.resolve(__dirname, \"src/components\"),\n      },\n    },\n  },\n});\n```\n\n### Adding Path Aliases (Rsbuild)\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  rsbuild: {\n    source: {\n      alias: {\n        \"@\": \"./src\",\n        \"@components\": \"./src/components\",\n      },\n    },\n  },\n});\n```\n\n### Adding Additional Vite Plugins\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\nimport { defineConfig } from \"vite\";\nimport UnoCSS from \"unocss/vite\";\n\nexport default viteConfig({\n  vite: {\n    plugins: [\n      UnoCSS(), // Add UnoCSS plugin\n    ],\n  },\n});\n```\n\n### Adding Additional Rsbuild Plugins\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\nimport { pluginSass } from \"@rsbuild/plugin-sass\";\n\nexport default rsbuildConfig({\n  rsbuild: {\n    plugins: [\n      pluginSass(), // Add Sass plugin\n    ],\n  },\n});\n```\n\n### Custom Plugin Manifest Path\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  manifestPath: \"application/src/main/resources/plugin.yaml\", // Custom manifest file path\n  vite: {\n    // Other configurations...\n  },\n});\n```\n\n### Custom Theme Manifest Path\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  provider: \"theme\",\n  manifestPath: \"../custom-theme.yaml\",\n  vite: {\n    // Other configurations...\n  },\n});\n```\n\n## Development Scripts\n\nRecommended scripts to add to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"vite dev --mode=development --watch\",\n    \"build\": \"vite build\"\n  }\n}\n```\n\nFor Rsbuild:\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"rsbuild dev --env-mode=development --watch\",\n    \"build\": \"rsbuild build\"\n  }\n}\n```\n\n## Build Output\n\n> Relative to the UI plugin project root\n\nPlugin provider:\n\n- **Development**: `../build/resources/main/ui` or `../build/resources/main/console`\n- **Production**: `./build/dist`\n\nTheme provider:\n\n- **Development**: `dist`\n- **Production**: `dist`\n\nAn ESM output additionally contains the reserved, generated `ui-plugin.json` manifest and may contain content-hashed `chunks/` and `assets/`. The manifest contains `format`, the actual content-hashed `entry`, and an optional content-hashed `style`; asynchronous chunk CSS is not listed. Keep the complete output directory together. Halo serves these canonical paths through the existing plugin or theme static resource mapping without adding query cache keys, so imports back to the entry resolve to the same ESM module URL. Callers that replace the default content-hashed filenames accept the risk of stale resources under Halo's production static-resource cache. Legacy IIFE resources retain their version query behavior. The Halo 2.x compatibility `bundle.css` endpoint remains available for older callers, but now contains ordered `@import` rules pointing at those direct styles so relative asset URLs keep the correct provider base; it is not used by the new runtime.\n\n## Maintaining Halo Host Runtime Snapshots\n\nSnapshots capture the exact host version, statically importable root exports that are also present on the browser runtime global, bridge global, and identity category for each shared root. Synthetic namespace metadata such as `__esModule` is excluded when the browser global does not expose it. Snapshots do not claim an accepted provider version range. They are intentionally sparse: add a new immutable snapshot when Halo's shared runtime contract changes, not automatically for every Halo patch. A newer target selects the latest eligible older snapshot and emits a forward-compatibility warning; a target older than every packaged snapshot cannot build ESM.\n\nWhen the host dependency graph changes:\n\n1. Set `@halo-dev/ui-plugin-bundler-kit` to the Halo version represented by the snapshot.\n2. Run `pnpm --filter @halo-dev/ui-plugin-bundler-kit snapshot:generate` to derive the versioned output path and capture resolved host versions and exports.\n3. Run `pnpm --filter @halo-dev/ui-plugin-bundler-kit snapshot:check` and the compatibility fixtures before publishing.\n4. Preserve older snapshot files while supported provider artifacts may still target them.\n\nSnapshot generation is currently an explicit maintainer action; it is not coupled to every package build or CI job.\n\n## Requirements\n\n- **Node.js**: ^18.0.0 || >=20.0.0\n- **Peer Dependencies**:\n  - `@rsbuild/core`: ^1.0.0 || ^2.0.0 (when using Rsbuild)\n  - `@rsbuild/plugin-vue`: ^1.0.0 || ^2.0.0 (when using Rsbuild)\n  - `@vitejs/plugin-vue`: ^5.0.0 || ^6.0.0 (when using Vite)\n  - `vite`: ^6.0.0 || ^7.0.0 || ^8.0.0 (when using Vite)\n\n## Choosing Between Vite and Rsbuild\n\n**Vite is the recommended default for new UI plugins.** Halo 2.26.0 and later can load ESM plugin resources, so Vite builds can use dynamic imports and emit separate asynchronous chunks. Rsbuild was originally introduced to support code splitting while UI plugins were limited to legacy IIFE output; that is no longer a reason to prefer Rsbuild when targeting Halo 2.26.0 or later.\n\n### Use Vite by Default\n\n- **ESM code splitting** - Supports lazy loading and asynchronous chunks when the target Halo version selects ESM output.\n- **Broader ecosystem** - Provides wider plugin coverage and closer alignment with Vue ecosystem tooling.\n- **Strong build performance** - Recent Vite versions have substantially improved build performance, so build speed alone is usually not a reason to choose Rsbuild.\n- **Familiar configuration** - Keeps plugin projects aligned with the tooling commonly used by Vue applications.\n\n### Use Rsbuild When Needed\n\nChoose Rsbuild when at least one of these conditions applies:\n\n- The plugin must keep legacy IIFE output, such as when targeting Halo versions earlier than 2.26.0, but still needs code splitting.\n- The project already uses Rsbuild or depends on Rsbuild/Rspack-specific plugins and configuration.\n- Measurements on the actual project show a meaningful build-performance advantage.\n\nDo not choose a build system based only on plugin size. With ESM output, both Vite and Rsbuild support dynamic imports and code splitting. The following pattern works with either preset:\n\n```typescript\nimport { definePlugin } from \"@halo-dev/ui-shared\";\nimport { defineAsyncComponent } from \"vue\";\nimport { VLoading } from \"@halo-dev/components\";\n\nexport default definePlugin({\n  routes: [\n    {\n      parentName: \"Root\",\n      route: {\n        path: \"demo\",\n        name: \"DemoPage\",\n        // Lazy load heavy components\n        component: defineAsyncComponent({\n          loader: () => import(\"./views/DemoPage.vue\"),\n          loadingComponent: VLoading,\n        }),\n      },\n    },\n  ],\n  extensionPoints: {},\n});\n```\n\n### Summary\n\n| Consideration              | Vite                                       | Rsbuild                                         |\n| -------------------------- | ------------------------------------------ | ----------------------------------------------- |\n| Recommended use            | New plugins targeting Halo 2.26.0 or later | Existing Rsbuild projects or legacy IIFE builds |\n| ESM code splitting         | Supported                                  | Supported                                       |\n| Legacy IIFE code splitting | Not supported by the default preset        | Supported                                       |\n| Plugin ecosystem           | Broader Vite and Vue ecosystem             | Smaller, focused on Rsbuild and Rspack          |\n| Build performance          | Fast; recommended default                  | Fast; benchmark when performance is decisive    |\n\n## License\n\nGPL-3.0\n\n## Contributing\n\nIssues and Pull Requests are welcome! Please check our [Contributing Guide](https://github.com/halo-dev/halo/blob/main/CONTRIBUTING.md) for more information.\n\n## Related Links\n\n- [Halo Website](https://www.halo.run/)\n- [Halo Documentation](https://docs.halo.run/)\n- [GitHub Repository](https://github.com/halo-dev/halo)\n- [Plugin Development Guide](https://docs.halo.run/developer-guide/plugin/basics/ui/)\n"},"files":{".claude/commands/opsx/apply.md":"---\nname: \"OPSX: Apply\"\ndescription: Implement tasks from an OpenSpec change (Experimental)\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using `/opsx:continue`\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! You can archive this change with `/opsx:archive`.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n",".claude/commands/opsx/archive.md":"---\nname: \"OPSX: Archive\"\ndescription: Archive a completed change in the experimental workflow\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, archive, experimental]\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Prompt user for confirmation to continue\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Prompt user for confirmation to continue\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Spec sync status (synced / sync skipped / no delta specs)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs\n\nAll artifacts complete. All tasks complete.\n```\n\n**Output On Success (No Delta Specs)**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** No delta specs\n\nAll artifacts complete. All tasks complete.\n```\n\n**Output On Success With Warnings**\n\n```\n## Archive Complete (with warnings)\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** Sync skipped (user chose to skip)\n\n**Warnings:**\n- Archived with 2 incomplete artifacts\n- Archived with 3 incomplete tasks\n- Delta spec sync was skipped (user chose to skip)\n\nReview the archive if this was not intentional.\n```\n\n**Output On Error (Archive Exists)**\n\n```\n## Archive Failed\n\n**Change:** <change-name>\n**Target:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n\nTarget archive directory already exists.\n\n**Options:**\n1. Rename the existing archive\n2. Delete the existing archive if it's a duplicate\n3. Wait until a different date to archive\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use the Skill tool to invoke `openspec-sync-specs` (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n",".claude/commands/opsx/explore.md":"---\nname: \"OPSX: Explore\"\ndescription: \"Enter explore mode - think through ideas, investigate problems, clarify requirements\"\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, explore, experimental, thinking]\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The argument after `/opsx:explore` is whatever the user wants to think about. Could be:\n- A vague idea: \"real-time collaboration\"\n- A specific problem: \"the auth system is getting unwieldy\"\n- A change name: \"add-dark-mode\" (to explore in context of that change)\n- A comparison: \"postgres vs sqlite for this\"\n- Nothing (just enter explore mode)\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\nIf the user mentioned a specific change name, read its artifacts for context.\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen things crystallize, you might offer a summary - but it's optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n",".claude/commands/opsx/propose.md":"---\nname: \"OPSX: Propose\"\ndescription: Propose a new change - create it and generate all artifacts in one step\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The argument after `/opsx:propose` is the change name (kebab-case), OR a description of what the user wants to build.\n\n**Steps**\n\n1. **If no input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` to start implementing.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n",".claude/commands/opsx/sync.md":"---\nname: \"OPSX: Sync\"\ndescription: Sync delta specs from a change to main specs\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, specs, experimental]\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n",".claude/commands/opsx/update.md":"---\nname: \"OPSX: Update\"\ndescription: Update a change - revise existing planning artifacts and keep them coherent (Experimental)\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name after `/opsx:update` (e.g., `/opsx:update add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n",".claude/skills/gradle-dependency-updates/SKILL.md":"---\nname: gradle-dependency-updates\ndescription: >-\n  Upgrade outdated Gradle dependencies in this project. Use this skill whenever\n  the user asks to check for dependency updates, show what's outdated, or\n  upgrade/bump/update Gradle dependencies, libraries, or plugins — including\n  mentions of \"dependencyUpdates\", the versions plugin, or \"outdated\n  dependencies\". Backend only (api/application/platform); frontend dependencies\n  (ui/, pnpm) are out of scope.\n---\n\n# Gradle Dependency Updates\n\nThis project manages backend dependencies through a Gradle version catalog\n(`gradle/libs.versions.toml`) plus the Spring Boot BOM. The ben-manes Versions\nplugin is already applied to `:api` and `:application`.\n\n## 1. Find what's outdated\n\n```bash\n./gradlew dependencyUpdates -DoutputFormatter=plain,json --console=plain\n```\n\nRead the reports at `api/build/dependencyUpdates/report.{txt,json}` and\n`application/build/dependencyUpdates/report.{txt,json}`; merge and dedupe\nacross the two modules.\n\nThe plugin's default revision includes stable releases, so \"later milestone\nversions\" does not mean pre-releases — only versions with a qualifier\n(alpha/beta/rc) are pre-releases.\n\n## 2. Decide what can be bumped\n\n- **Catalog library** — declared in `[libraries]` of `gradle/libs.versions.toml`\n  → bumpable in the catalog.\n- **Gradle plugin** — declared in `[plugins]` → bumpable in the catalog.\n- **BOM-managed** — pinned by the Spring Boot BOM in `platform/application`\n  (Spring Framework, r2dbc drivers, postgresql, byte-buddy, jspecify,\n  junit-platform-launcher, ...) → never bump individually; they arrive with the\n  next Spring Boot BOM bump.\n- **Gradle wrapper** — `gradle/wrapper/gradle-wrapper.properties`.\n\n## 3. Project constraints (do not cross)\n\n- **thymeleaf**: the `runtimeOnly ':thymeleaf:...'` flat-dir jars in\n  `application/build.gradle` are a pinned workaround (halo-dev/halo#7289) —\n  never touch them.\n- **lombok**: keep aligned with the `io.freefair.lombok` plugin; do not add a\n  `lombok { version = ... }` override.\n- **ben-manes Versions plugin itself**: stay on 0.54.0 — 0.59.0 fails to apply\n  with a classloader error.\n- **Pre-releases** (e.g. tika-core 4.0.0-beta-1): skip unless the user asks.\n- Versions live only in `gradle/libs.versions.toml`, `gradle.properties`, or\n  `ui/package.json` — never hard-code them (AGENTS.md).\n\n## 4. Show the update plan\n\nGroup by category, current → latest with the semver delta (major/minor/patch),\nstate where each bump lands, and list excluded items with reasons.\n\n## 5. Apply and verify\n\nEdit `gradle/libs.versions.toml` — one line per bump — only after the user\napproves the plan, then confirm the build:\n\n```bash\n./gradlew build\n```\n\nRe-run `dependencyUpdates` and check that the bumped items no longer appear as\noutdated; what remains should be exactly the out-of-scope set.\n",".claude/skills/openspec-apply-change/SKILL.md":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n",".claude/skills/openspec-archive-change/SKILL.md":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n",".claude/skills/openspec-explore/SKILL.md":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n",".claude/skills/openspec-propose/SKILL.md":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n",".claude/skills/openspec-sync-specs/SKILL.md":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n",".claude/skills/openspec-update-change/SKILL.md":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n",".codex/skills/openspec-apply-change/SKILL.md":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n",".codex/skills/openspec-archive-change/SKILL.md":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n",".codex/skills/openspec-explore/SKILL.md":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n",".codex/skills/openspec-propose/SKILL.md":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n",".codex/skills/openspec-sync-specs/SKILL.md":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n",".codex/skills/openspec-update-change/SKILL.md":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n",".github/skills/openspec-apply-change/SKILL.md":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n",".github/skills/openspec-archive-change/SKILL.md":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n",".github/skills/openspec-explore/SKILL.md":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n",".github/skills/openspec-propose/SKILL.md":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n",".github/skills/openspec-sync-specs/SKILL.md":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n",".github/skills/openspec-update-change/SKILL.md":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n","AGENTS.md":"# Repository Guidelines\n\nHalo is a full-stack monorepo: Java 21 / Spring Boot WebFlux + R2DBC backend and a Vue 3 + TypeScript + TailwindCSS frontend. Work from the repo root and load **every relevant module guide** for the areas you touch.\n\n## Modules\n\n|       Module        |                           Path                            |\n|---------------------|-----------------------------------------------------------|\n| API library         | `api/` (extension model, contracts, security)             |\n| Backend application | `application/` (services, routers, migrations, packaging) |\n| Frontend            | `ui/` (console, user center, workspace packages)          |\n| Application BOM     | `platform/application/` (shared dependency constraints)   |\n| Plugin BOM          | `platform/plugin/` (plugin-facing constraints)            |\n\n## Quick Commands\n\nRun from the repo root:\n\n- `./gradlew build` — full backend build and tests.\n- `./gradlew spotlessApply` — format Java, Markdown, JSON, and properties.\n- `./gradlew :application:test` — backend tests.\n- `pnpm -C ui typecheck && pnpm -C ui lint` — frontend validation.\n- `pnpm -C ui test:unit` — frontend unit tests.\n- `./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen` — regenerate the UI client after contract changes.\n\n## Cross-Module Rules\n\n- Keep API contracts, backend handlers, and UI usage in sync; never hand-edit `ui/packages/api-client/src/` — regenerate it after contract changes.\n- Versions live in `gradle/libs.versions.toml`, `gradle.properties`, or `ui/package.json`; never hard-code them.\n- Branch naming: `feat/`, `fix/`, `improvement/`, `upgrade/`; PRs target `upstream` and follow `.github/pull_request_template.md`.\n- Stage specific files only; never `git add -A` (H2 artifacts and build outputs appear under `application/`).\n- Add tests for changed behavior; keep CI green.\n- Ask before public API changes, new dependencies, database migrations, security configuration, or CI workflow changes.\n- Never commit secrets or introduce blocking I/O into reactive flows.\n\n","CLAUDE.md":"@AGENTS.md\n","api/AGENTS.md":"# API Library Guidelines\n\n`api/` is the public API library: the extension model, service contracts, and security abstractions consumed by the application and by plugins. Changes here affect downstream compatibility.\n\n## Scope\n\n- Source: `api/src/main/java/run/halo/app/` (core, extension, plugin, security, migration, theme, and related packages).\n- Tests: `api/src/test/`.\n\n## Commands\n\n- `./gradlew :api:test` — run API library tests.\n- `./gradlew spotlessApply` — format Java and Markdown.\n\n## Conventions\n\n- Java 21, 4-space indentation, formatted with Spotless.\n- The API is a compatibility surface: prefer additive changes, and flag any breaking signature or contract change for review before merging.\n- Extension definitions and schemas live in `application/src/main/resources/extensions/`; when you change them, update `application/` and `ui/` together.\n- Tests: JUnit 5, named `XxxTest` (unit) or `XxxIntegrationTest` (integration).\n\n## Boundaries\n\n- Public API changes in `api/` require explicit approval — treat them as affecting every plugin built against Halo.\n\n","api/CLAUDE.md":"@AGENTS.md\n","application/AGENTS.md":"# Backend Application Guidelines\n\n`application/` is the WebFlux + R2DBC backend: services, routers, security, migrations, and packaging. Everything here is reactive — never block threads.\n\n## Structure\n\n- `application/src/main/java/` — services, routers, endpoints, and configuration.\n- `application/src/main/resources/application*.yaml` — environment configuration.\n- `application/src/main/resources/db/migration/{h2,mariadb,mysql,postgresql}/` — per-dialect migrations.\n- `application/src/main/resources/extensions/` — extension schemas and role templates.\n- `application/src/test/` — backend tests.\n\n## Commands\n\n- `./gradlew :application:test` — run backend tests.\n- `./gradlew :application:bootRun` — start the backend dev server.\n- `./gradlew :application:copyUiDist` — embed the built UI into the JAR.\n- `./gradlew generateOpenApiDocs` — emit the OpenAPI spec used to regenerate the UI client.\n\n## Conventions\n\n- Keep reactive flows non-blocking; no blocking I/O in request paths.\n- Database migrations are versioned per dialect — never auto-generate them, and review them with the `application/` guide in mind.\n- Tests: JUnit 5 with Spring Boot test support; name unit tests `XxxTest` and integration tests `XxxIntegrationTest`.\n- After DTO, route, or schema changes, regenerate the UI client (`./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen`) and update `ui/` usage.\n\n## Boundaries\n\n- Ask before adding database migrations, changing security configuration, or introducing new dependencies.\n\n","application/CLAUDE.md":"@AGENTS.md\n","ui/AGENTS.md":"# Frontend Guidelines\n\n`ui/` is the Vue 3 + TypeScript + TailwindCSS frontend, managed with pnpm workspaces.\n\n## Structure\n\n- `ui/console-src/` — console application (entry: `console.html`, alias `@console`).\n- `ui/uc-src/` — user center application (entry: `uc.html`, alias `@uc`).\n- `ui/src/` — shared code: build-time Vite helpers (`src/vite/`), locales, assets, and shared views (alias `@`); not an application entry point.\n- `ui/packages/` — shared workspace packages (`@halo-dev/api-client`, `@halo-dev/components`, `@halo-dev/console-shared`, `@halo-dev/editor`, `@halo-dev/shared`, `@halo-dev/ui-plugin-bundler-kit`).\n- Unit tests: `*.spec.ts` files, usually under `__tests__/` next to the code they cover.\n\n## Commands\n\nRun from the repo root:\n\n- `pnpm -C ui install` / `pnpm -C ui dev` — install dependencies / run the dev server.\n- `pnpm -C ui build` — production build.\n- `pnpm -C ui typecheck` — `vue-tsc` type checking.\n- `pnpm -C ui lint` — ESLint (fails on any warning).\n- `pnpm -C ui format` / `pnpm -C ui format:check` — Prettier formatting.\n- `pnpm -C ui test:unit` — Vitest unit tests.\n- `./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen` — regenerate `ui/packages/api-client` after backend contract changes.\n\n## Conventions\n\n- Follow existing component and composable patterns; match surrounding code rather than introducing new stylistic islands.\n- Never hand-edit `ui/packages/api-client/src/` — it is generated; regenerate it instead.\n- Keep application dependency versions in `ui/package.json`. For dependencies shared by workspace packages, define versions in the catalog in `ui/pnpm-workspace.yaml` and reference them with `catalog:`; never duplicate version declarations across packages.\n- Payload or UX changes usually ship together with the backend change that defines the contract.\n\n## Boundaries\n\n- New npm dependencies require approval.\n","ui/CLAUDE.md":"@AGENTS.md\n","ui/packages/api-client/README.md":"# @halo-dev/api-client\n\nHalo 2.0 的 JavaScript API 客户端请求库。使用 [OpenAPI Generator](https://openapi-generator.tech/) 生成。\n\n## 使用\n\n```javascript\nimport {\n  coreApiClient,\n  consoleApiClient,\n  ucApiClient,\n  publicApiClient,\n  createCoreApiClient,\n  createConsoleApiClient,\n  createUcApiClient,\n  createPublicApiClient,\n  axiosInstance,\n} from \"@halo-dev/api-client\";\n```\n\n- **coreApiClient**: 为 Halo 所有自定义模型的 CRUD 接口封装的 api client。\n- **consoleApiClient**: 为 Halo 针对 Console 提供的接口封装的 api client。\n- **ucApiClient**: 为 Halo 针对 UC 提供的接口封装的 api client。\n- **publicApiClient**: 为 Halo 所有公开访问的接口封装的 api client。\n- **createCoreApiClient**: 用于创建自定义模型的 CRUD 接口封装的 api client，需要传入 axios 实例。\n- **createConsoleApiClient**: 用于创建 Console 接口封装的 api client，需要传入 axios 实例。\n- **createUcApiClient**: 用于创建 UC 接口封装的 api client，需要传入 axios 实例。\n- **createPublicApiClient**: 用于创建公开访问接口封装的 api client，需要传入 axios 实例。\n- **axiosInstance**: 内部默认创建的 axios 实例。\n\n### 在插件中使用\n\n```shell\npnpm install @halo-dev/api-client axios\n```\n\n由于已经在 Console 和 UC 项目中引入并设置好了 Axios 拦截器，所以直接使用即可：\n\n```javascript\nimport { coreApiClient } from \"@halo-dev/api-client\";\n\ncoreApiClient.content.post.listPost().then((response) => {\n  // handle response\n});\n```\n\n此外，在最新的 `@halo-dev/ui-plugin-bundler-kit@2.17.0` 中，已经排除了 `@halo-dev/api-client`、`axios` 依赖，所以最终产物中的相关依赖会自动使用 Halo 本身提供的依赖，无需关心最终产物大小。\n\n详细文档可查阅：[插件开发 / API 请求](https://docs.halo.run/developer-guide/plugin/api-reference/ui/api-request)\n\n### 在外部项目中使用\n\n```shell\npnpm install @halo-dev/api-client axios\n```\n\n```javascript\nimport axios from \"axios\";\n\nconst axiosInstance = axios.create({\n  baseURL: \"http://localhost:8090\",\n});\n\nconst coreApiClient = createCoreApiClient(axiosInstance);\n\ncoreApiClient.content.post.listPost().then((response) => {\n  // handle response\n});\n```\n","ui/packages/console-shared/README.md":"Please use `@halo-dev/ui-shared`, this package is only for compatibility.\n","ui/packages/editor/README.md":"# @halo-dev/richtext-editor\n\nThe default editor for Halo, built with [Tiptap](https://tiptap.dev/) + [ProseMirror](https://prosemirror.net/).\n\n> ⚠️ This package is not suitable for general editor use cases, as it is tightly coupled with Halo and uses some of Halo's built-in tools and shared dependencies.\n> However, you can use it in Halo plugins just like you would use it in Halo itself.\n\n## Installation\n\nIn Halo plugins:\n\n```bash\npnpm install @halo-dev/richtext-editor\n```\n\n## Usage\n\n```vue\n<script setup lang=\"ts\">\nimport {\n  ExtensionsKit,\n  RichTextEditor,\n  VueEditor,\n} from \"@halo-dev/richtext-editor\";\nimport { onMounted, ref, shallowRef } from \"vue\";\n\nconst editor = shallowRef<VueEditor>();\nconst content = ref(\"Hello World\");\n\nonMounted(() => {\n  editor.value = new VueEditor({\n    content: content.value,\n    extensions: [ExtensionsKit],\n    parseOptions: {\n      preserveWhitespace: true,\n    },\n    onUpdate: () => {\n      content.value = editor.value?.getHTML() || \"\";\n    },\n  });\n});\n</script>\n<template>\n  <RichTextEditor v-if=\"editor\" :editor=\"editor\" />\n</template>\n```\n\n## Requirements\n\n- Vue 3.5.x or higher\n- Halo plugin environment\n\n## Extension development\n\n- [Editor extensions, toolbar items, and keyboard shortcuts](./docs/extension.md)\n- [Editor extension runtime metadata](./docs/runtime-metadata.md)\n\n## Links\n\n- [Halo](https://github.com/halo-dev/halo)\n- [Documentation](https://docs.halo.run)\n- [Report Issues](https://github.com/halo-dev/halo/issues)\n\n## License\n\nGPL-3.0\n","ui/packages/editor/docs/extension.md":"# 扩展说明\n\n本文档介绍如何对编辑器的功能进行扩展，包括但不限于扩展工具栏、悬浮工具栏、Slash Command、拖拽功能等。各扩展区域参考下图：\n\n![编辑器扩展说明](extension.png)\n\n目前支持的所有扩展类型 [ExtensionOptions](../packages/editor/src/types/index.ts) 如下所示：\n\n```ts\nexport interface ExtensionOptions {\n  // 顶部工具栏扩展\n  getToolbarItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => ToolbarItemType | ToolbarItemType[];\n\n  // Slash Command 扩展\n  getCommandMenuItems?: () => CommandMenuItemType | CommandMenuItemType[];\n\n  // 悬浮菜单扩展\n  getBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenuType;\n\n  // 工具箱扩展\n  getToolboxItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => ToolboxItemType | ToolboxItemType[];\n\n  // 拖拽菜单扩展\n  getDraggableMenuItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => DragButtonType | DragButtonType[];\n}\n```\n\n> 对于 Tiptap 本身的扩展方式可以参考 <https://tiptap.dev/api/introduction>\n\n## 1. 顶部工具栏扩展\n\n编辑器顶部功能区域内容的扩展，通常用于增加用户常用操作，例如文本加粗、变更颜色等。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/16> 中，我们实现了对顶部工具栏的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolbarItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolbarItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getToolbarItems` 即为对顶部工具栏的扩展。其返回类型为：\n\n```ts\n// 顶部工具栏扩展\ngetToolbarItems?: ({\n  editor,\n}: {\n  editor: Editor;\n}) => ToolbarItemType | ToolbarItemType[];\n\n// 工具栏\nexport interface ToolbarItemType {\n  priority: number;\n  component: Component;\n  props: {\n    editor: Editor;\n    isActive: boolean;\n    disabled?: boolean;\n    icon?: Component;\n    title?: string;\n    shortcutId?: string;\n    shortcutIds?: string[];\n    action?: () => void;\n  };\n  children?: ToolbarItemType[];\n}\n```\n\n如下为 [`Bold`](../packages/editor/src/extensions/bold/index.ts) 扩展中对于 `getToolbarItems` 的扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n    getToolbarItems({ editor }: { editor: Editor }) {\n      return {\n        priority: 40,\n        component: markRaw(ToolbarItem),\n        props: {\n          editor,\n          isActive: editor.isActive(\"bold\"),\n          icon: markRaw(MdiFormatBold),\n          title: i18n.global.t(\"editor.common.bold\"),\n          action: () => editor.chain().focus().toggleBold().run(),\n        },\n      };\n    },\n  };\n},\n```\n\n## 2. 快捷键与提示信息\n\nHalo 在 Tiptap 的 `addKeyboardShortcuts` 基础上提供了快捷键描述注册表。第三方扩展仍然只需要实现一个 `addKeyboardShortcuts`，同时即可获得以下能力：\n\n- 执行 Tiptap 快捷键命令；\n- 在“键盘快捷键”侧边栏中展示操作说明；\n- 通过同一个 `shortcutId` 在工具栏或悬浮菜单的 tooltip 中展示快捷键；\n- 根据当前操作系统将 `Mod`、`Alt` 等按键格式化为对应的展示形式。\n\n### 2.1 注册快捷键并关联工具栏\n\n使用 `defineHaloKeyboardShortcuts` 定义快捷键，再将相同的 `id` 传给工具栏组件的 `shortcutId`：\n\n```ts\nimport {\n  defineHaloKeyboardShortcuts,\n  Extension,\n  ToolbarItem,\n  type Editor,\n  type ExtensionOptions,\n} from \"@halo-dev/richtext-editor\";\nimport { markRaw } from \"vue\";\nimport MyIcon from \"./MyIcon.vue\";\n\nconst shortcutId = \"plugin.example.insertGreeting\";\n\nfunction insertGreeting(editor: Editor) {\n  return editor.chain().focus().insertContent(\"Hello Halo\").run();\n}\n\nexport const ExtensionExample = Extension.create<ExtensionOptions>({\n  name: \"exampleShortcut\",\n\n  addKeyboardShortcuts() {\n    return defineHaloKeyboardShortcuts(this, [\n      {\n        id: shortcutId,\n        keys: [\"Mod-Alt-g\"],\n        label: \"插入问候语\",\n        category: \"general\",\n        priority: 100,\n        command: () => insertGreeting(this.editor),\n      },\n    ]);\n  },\n\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolbarItems({ editor }: { editor: Editor }) {\n        return {\n          priority: 100,\n          component: markRaw(ToolbarItem),\n          props: {\n            editor,\n            isActive: false,\n            icon: markRaw(MyIcon),\n            title: \"插入问候语\",\n            shortcutId,\n            action: () => insertGreeting(editor),\n          },\n        };\n      },\n    };\n  },\n});\n```\n\n`ToolbarItem`、`ToolbarSubItem` 和 `BubbleItem` 都支持 `shortcutId`。`ToolbarItem` 还支持 `shortcutIds`，适用于一个按钮对应多个操作的情况，例如同时展示“增大字号”和“减小字号”。tooltip 会展示每个快捷键定义中的第一组按键，快捷键侧边栏则会展示 `keys` 中的全部可选按键。\n\n默认编辑器已经通过 `ExtensionsKit` 内置 `ExtensionKeyboardShortcuts`，插件通过 `default:editor:extension:create` 扩展点注册时无需重复添加。自行创建编辑器实例时，应使用 `ExtensionsKit`，或显式加入 `ExtensionKeyboardShortcuts`。\n\n### 2.2 描述字段\n\n`defineHaloKeyboardShortcuts` 接收的每一项都是一个 `HaloKeyboardShortcutDefinition`：\n\n| 字段 | 必填 | 说明 |\n| --- | --- | --- |\n| `id` | 是 | 编辑器内稳定且唯一的标识符，用于关联 tooltip。插件应使用包含插件标识的命名空间，例如 `plugin.example.insertGreeting`。 |\n| `keys` | 是 | Tiptap 格式的按键组合。第一项是 tooltip 展示的主快捷键，全部按键都会展示在快捷键侧边栏中。 |\n| `label` | 是 | 用户可见的操作名称，可以是字符串或返回字符串的函数。 |\n| `category` | 是 | 快捷键侧边栏分组：`general`、`formatting`、`structure` 或 `navigation`。 |\n| `command` | 视情况 | 新增快捷键时必须提供。扩展已有 Tiptap 快捷键时可以省略，此时复用父扩展中相同按键的命令。 |\n| `description` | 否 | 操作的补充说明，可以是字符串或返回字符串的函数。 |\n| `priority` | 否 | 在快捷键侧边栏同一分组中的排序值，数值越小越靠前，默认为 `100`。 |\n| `discoverable` | 否 | 是否出现在快捷键侧边栏中，默认为 `true`。即使设为 `false`，显式绑定了 `shortcutId` 的 tooltip 仍可展示。 |\n| `visible` | 否 | 根据当前编辑器状态决定是否出现在快捷键侧边栏中。 |\n\n按键名称遵循 [Tiptap 快捷键格式](https://tiptap.dev/docs/editor/core-concepts/keyboard-shortcuts)。建议使用 `Mod` 表示 macOS 的 `Command` 和 Windows/Linux 的 `Control`，例如 `Mod-b`。命令处理成功时应返回 `true`，这样 ProseMirror 会阻止浏览器继续执行同一按键的默认行为；未处理时应返回 `false`。\n\n如果扩展继承的 Tiptap 扩展已经实现了相同按键，可以只补充 Halo 的描述信息，不需要重新实现命令：\n\n```ts\naddKeyboardShortcuts() {\n  return defineHaloKeyboardShortcuts(this, [\n    {\n      id: \"plugin.example.toggleFeature\",\n      keys: [\"Mod-b\"],\n      label: \"切换示例功能\",\n      category: \"formatting\",\n    },\n  ]);\n},\n```\n\n只有父扩展确实定义了 `keys` 中对应的按键时才能省略 `command`。开发环境会对缺少实际命令的定义输出警告，并且不会注册这条描述。\n\n### 2.3 自定义组件中的 tooltip\n\n完全自定义工具栏组件时，可以通过 `useHaloKeyboardShortcut` 响应式读取注册表，再使用 `KeyboardShortcutTooltip` 保持与内置工具栏一致的视觉和无障碍信息：\n\n```vue\n<script setup lang=\"ts\">\nimport {\n  KeyboardShortcutTooltip,\n  useHaloKeyboardShortcut,\n  type Editor,\n} from \"@halo-dev/richtext-editor\";\n\nconst props = defineProps<{\n  editor: Editor;\n  shortcutId: string;\n  title: string;\n}>();\n\nconst shortcut = useHaloKeyboardShortcut(props.editor, () => props.shortcutId);\n</script>\n\n<template>\n  <KeyboardShortcutTooltip\n    v-slot=\"tooltipProps\"\n    :title=\"title\"\n    :shortcut=\"shortcut?.keys[0]\"\n  >\n    <button :aria-label=\"tooltipProps.ariaLabel\" type=\"button\">\n      {{ title }}\n    </button>\n  </KeyboardShortcutTooltip>\n</template>\n```\n\n一个组件需要读取多条快捷键时，可以使用 `useHaloKeyboardShortcuts(editor, () => shortcutIds)`。这两个 composable 必须在 Vue 组件的 `setup` 阶段调用，以便组件卸载时自动取消注册表订阅。\n\n### 2.4 命名与冲突规则\n\n- `id` 应包含插件标识，避免覆盖其他扩展注册的描述；快捷键注册表不会自动为重复 ID 添加命名空间。\n- 只注册产品中真实可执行的快捷键，不要为了填满快捷键侧边栏而自行创造按键组合。\n- 添加按键前应检查 Halo 默认快捷键、Tiptap 默认快捷键以及浏览器常用快捷键。确实需要覆盖浏览器默认行为时，命令必须在成功处理后返回 `true`。\n- `label` 和 `description` 应面向用户描述操作，不要使用内部命令名或扩展名。\n\n## 3. 工具箱扩展\n\n编辑器工具箱区域的扩展，可用于增加编辑器附属操作，例如插入表格，插入第三方组件等功能。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/27> 中，我们实现了对编辑器工具箱区域的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolboxItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolboxItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getToolboxItems` 即为对工具箱的扩展。其返回类型为：\n\n```ts\n// 工具箱扩展\ngetToolboxItems?: ({\n  editor,\n}: {\n  editor: Editor;\n}) => ToolboxItemType | ToolboxItemType[];\n\nexport interface ToolboxItemType {\n  priority: number;\n  component: Component;\n  props: {\n    editor: Editor;\n    icon?: Component;\n    title?: string;\n    description?: string;\n    action?: () => void;\n  };\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getToolboxItems` 工具箱的扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n    getToolboxItems({ editor }: { editor: Editor }) {\n      return {\n        priority: 15,\n        component: markRaw(ToolboxItem),\n        props: {\n          editor,\n          icon: markRaw(MdiTablePlus),\n          title: i18n.global.t(\"editor.menus.table.add\"),\n          action: () =>\n            editor\n              .chain()\n              .focus()\n              .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n              .run(),\n        },\n      };\n    },\n  }\n}\n```\n\n## 4. Slash Command 扩展\n\nSlash Command （斜杠命令）的扩展，可用于在当前行快捷执行功能操作，例如转换当前行为标题、在当前行添加代码块等功能。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/16> 中，我们实现了对 Slash Command 指令的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getCommandMenuItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getCommandMenuItems() {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getCommandMenuItems` 即为对工具箱的扩展。其返回类型为：\n\n```ts\n// Slash Command 扩展\ngetCommandMenuItems?: () => CommandMenuItemType | CommandMenuItemType[];\n\nexport interface CommandMenuItemType {\n  priority: number;\n  icon: Component;\n  title: string;\n  keywords: string[];\n  command: ({ editor, range }: { editor: Editor; range: Range }) => void;\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getCommandMenuItems` 的扩展示例：\n\n```ts\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getCommandMenuItems() {\n        return {\n          priority: 120,\n          icon: markRaw(MdiTable),\n          title: \"editor.extensions.commands_menu.table\",\n          keywords: [\"table\", \"biaoge\"],\n          command: ({ editor, range }: { editor: Editor; range: Range }) => {\n            editor\n              .chain()\n              .focus()\n              .deleteRange(range)\n              .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n              .run();\n          },\n        };\n      },\n    }\n  }\n```\n\n## 5. 悬浮菜单扩展\n\n编辑器悬浮菜单的扩展。可用于支持目标元素组件的功能扩展及操作简化。例如 `Table` 扩展中的添加下一列、添加上一列等操作。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/38> 中，我们重构了对编辑器悬浮区域的扩展，如果需要对某个块进行支持，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getBubbleMenu` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getBubbleMenu({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getBubbleMenu` 即为对悬浮菜单的扩展。其返回类型为：\n\n```ts\n// 悬浮菜单扩展\ngetBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenuType;\n\ninterface BubbleMenuProps {\n  pluginKey?: string;                                             // 悬浮菜单插件 Key，建议命名方式 xxxBubbleMenu\n  editor?: Editor;\n  shouldShow: (props: {                                           // 悬浮菜单显示的条件\n    editor: Editor;\n    state: EditorState;\n    node?: HTMLElement;\n    view?: EditorView;\n    oldState?: EditorState;\n    from?: number;\n    to?: number;\n  }) => boolean;\n  tippyOptions?: Record<string, unknown>;                          // 可自由定制悬浮菜单所用的 tippy 组件的选项\n  getRenderContainer?: (node: HTMLElement) => HTMLElement;         // 悬浮菜单所基准的 DOM\n  defaultAnimation?: boolean;                                      // 是否启用默认动画。默认为 true\n}\n\n// 悬浮菜单\nexport interface NodeBubbleMenuType extends BubbleMenuProps {\n  component?: Component;                                           // 不使用默认的样式，与 items 二选一\n  items?: BubbleItemType[];                                       // 悬浮菜单子项，使用默认的形式进行，与 items 二选一\n  extendsKey?: string | PluginKey;                                 // 用于扩展已有悬浮菜单的 key，如果未提供，则会被视为一个新的悬浮菜单\n}\n\n// 悬浮菜单子项\nexport interface BubbleItemType {\n  priority: number;                                                // 优先级，数字越小优先级越大，越靠前\n  component?: Component;                                           // 完全自定义子项样式\n  key?: string;                                                    // 子项的唯一标识，通常用于扩展悬浮菜单时仅保留唯一的子项。\n  props?: {                                                        // 子项属性，可选。同时支持传入自定义属性\n    isActive?: ({ editor }: { editor: Editor }) => boolean;         // 当前功能是否已经处于活动状态\n    visible?: ({ editor }: { editor: Editor }) => boolean;         // 是否显示当前子项\n    icon?: Component;                                              // 图标\n    iconStyle?: string;                                            // 图标自定义样式\n    title?: string;                                                // 标题\n    action?: ({ editor }: { editor: Editor }) => Component | void; // 点击子项后的操作，如果返回 Component，则会将其包含在下拉框中。\n  } & Record<string, unknown>;\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getBubbleMenu` 悬浮菜单的部分扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n      getBubbleMenu({ editor }) {\n        return {\n          pluginKey: \"tableBubbleMenu\",\n          shouldShow: ({ state }: { state: EditorState }): boolean => {\n            return isActive(state, Table.name);\n          },\n          getRenderContainer(node) {\n            let container = node;\n            if (container.nodeName === \"#text\") {\n              container = node.parentElement as HTMLElement;\n            }\n            while (\n              container &&\n              container.classList &&\n              !container.classList.contains(\"tableWrapper\")\n            ) {\n              container = container.parentElement as HTMLElement;\n            }\n            return container;\n          },\n          tippyOptions: {\n            offset: [26, 0],\n          },\n          items: [\n            {\n              priority: 10,\n              props: {\n                icon: markRaw(MdiTableColumnPlusBefore),\n                title: i18n.global.t(\"editor.menus.table.add_column_before\"),\n                action: () => editor.chain().focus().addColumnBefore().run(),\n              },\n            },\n          ]\n        }\n      }\n  }\n}\n```\n\n## 6. 拖拽菜单扩展\n\n拖拽菜单扩展主要用于拖拽的菜单功能扩展，例如转换为、复制、剪切、删除等操作。\n\n在 <https://github.com/halo-dev/halo/pull/7861> 中，我们重构了对编辑器拖拽区域的扩展，并且支持了对拖拽菜单的扩展。如果需要对拖拽菜单进行扩展，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getDraggableMenuItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n同时，为了支持不同扩展对同一菜单项的扩展，我们提供了 `extendsKey` 属性，用于指定扩展目标菜单项的唯一标识。只需将 `extendsKey` 设置为已有的菜单项的 `key`，即可扩展该菜单项。可扩展已有菜单项的 `visible`、`isActive`、`disabled`、`action` 方法以及 `children.items` 属性，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return {\n          extendsKey: CONVERT_TO_KEY,\n          // 当任意扩展目标菜单项的 visible 方法返回 false 时，当前菜单项不会显示。返回 true 则会继续执行后续的扩展实现。\n          visible: ({ editor }) => {\n            if (isActive(editor.state, \"table\")) {\n              return false;\n            }\n            return true;\n          },\n        };\n      },\n    };\n  },\n};\n```\n\n拖拽菜单最多支持两级菜单嵌套， 如果想扩展已有的一级菜单，为其二级菜单增加内容，则需要同时设置 `extendsKey` 和 `children.items` 属性。如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return {\n          extendsKey: CONVERT_TO_KEY,\n          children: {\n            items: [\n              {\n                priority: 10,\n                icon: markRaw(MdiFormatParagraph),\n                title: i18n.global.t(\"editor.common.heading.paragraph\"),\n                action: ({ editor }: { editor: Editor }) =>\n                  editor.chain().focus().setParagraph().run(),\n              },\n            ],\n          },\n        }\n      },\n    };\n  },\n}\n```\n\n默认情况下，将会追加 `items`，若想覆盖，则需要设置子菜单的 `key` 属性，将会覆盖原有的子菜单项。\n\n下面为 `getDraggableMenuItems` 的返回类型：\n\n```ts\n\n// 拖拽菜单扩展\ngetDraggableMenuItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => DragButtonType | DragButtonType[];\n\n// 拖拽菜单项目属性\nexport interface DragButtonItemProps {\n  extendsKey?: string;                                    // 扩展目标菜单项的唯一标识，如果提供了该属性，则视为扩展目标菜单项。\n  key?: string;                                           // 唯一标识，如果同级菜单项设置了同样的 key，则会被合并为一个菜单项。\n  priority?: number;                                      // 优先级，数字越小优先级越大，越靠前\n  title?: string | (() => string);                        // 标题\n  icon?: Component;                                       // 图标\n  action?: ({                                             // 点击菜单后的操作，如果返回 Component，则会将其包含在子菜单中。\n                                                          // 可以通过调用 close 方法可以在操作完成后关闭拖拽菜单，或者当返回为 true 或 undefined 时，会自动关闭拖拽菜单，如果返回 false，则不会关闭拖拽菜单。\n                                                          // 多个扩展实现时，则按照顺序执行，并在返回非 undefined 值时停止执行。\n    editor,\n    node,\n    pos,\n    close,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n    close: () => void;\n  }) => Component | boolean | void | Promise<Component | boolean | void>;\n  iconStyle?: string;                                       // 图标自定义样式\n  class?: string;                                           // 自定义样式\n  visible?: ({                                              // 是否显示当前菜单项，默认为 true，多个扩展实现时，以 AND 逻辑判断，即所有扩展返回 true 时，当前菜单项才会显示。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  isActive?: ({                                             // 当前菜单项是否处于活动状态，默认为 false，多个扩展实现时，以 OR 逻辑判断，即只要有一个扩展返回 true，则当前菜单项处于活动状态。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  disabled?: ({                                                // 是否禁用当前菜单项，默认为 false，多个扩展实现时，以 OR 逻辑判断，即只要有一个扩展返回 true，则当前菜单项会被禁用。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  keyboard?: string;                                            // 快捷键，遵循 https://tiptap.dev/docs/editor/core-concepts/keyboard-shortcuts\n  component?: Component;                                        // 自定义组件，如果提供了该属性，则不会显示默认的菜单项，而是会显示自定义组件，并且将所有 props 传递给自定义组件。\n  [key: string]: any;                                           // 其他自定义属性，将会传递给自定义组件。\n}\n\n// 一级菜单项\nexport interface DragButtonType extends DragButtonItemProps {\n  children?: {                                                    // 子菜单项，如果提供了该属性，则视为扩展目标菜单项的二级菜单。\n    component?: Component;                                        // 自定义组件，如果提供了该属性，则不会显示默认的子菜单项，而是会显示自定义组件，并且将所有 props 传递给自定义组件。\n    items?: DragButtonItemProps[];                                // 子菜单项列表，如果提供了该属性，则视为扩展目标菜单项的二级菜单。\n  };\n}\n```\n\n## 7. 块缩进扩展\n\n编辑器会根据节点的 schema 元数据发现可缩进节点，不维护组件名称白名单。第三方节点只要属于 `block` group，且不属于 `list` group，就会自动获得块缩进属性、快捷键和拖拽缩进能力：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MyBlock = Node.create({\n  name: \"myBlock\",\n  group: \"block\",\n  // ...\n});\n```\n\n特殊节点可以通过 `haloEditorIndentation` 调整接入行为：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MyBlock = Node.create({\n  name: \"myBlock\",\n  group: \"block\",\n\n  // 光标在节点内部时，将 Tab / Shift-Tab 交给节点自身处理；\n  // 节点选中或光标位于节点左上角间隙时，仍可缩进整个节点。\n  haloEditorIndentation: {\n    keyboard: \"passthrough\",\n  },\n});\n```\n\n- 设置为 `false` 可以让 block 节点退出通用缩进。\n- 设置为 `true` 可以让不属于 block group 的特殊节点显式接入缩进。\n- `legacyLineIndent: true` 仅用于需要兼容旧版首行缩进数据的文本节点，不建议新扩展启用。\n\n自定义列表容器应加入 `list` group，列表项应加入 `listItem` group。编辑器通过这两个 schema group 识别列表层级、继承缩进和行内拖拽目标，不依赖 `bulletList`、`orderedList`、`listItem` 等具体节点名称。\n\n第三方块命令可以复用以下公共 helpers：\n\n```ts\nimport {\n  findAncestorListItems,\n  getBlockIndentAtSelection,\n  prepareBlockCommandFromList,\n} from \"@halo-dev/richtext-editor\";\n\nconst listItems = findAncestorListItems(editor.state.selection.$from);\nconst indent = getBlockIndentAtSelection(editor);\nconst preparedRange = prepareBlockCommandFromList(editor, range);\n```\n\n- `findAncestorListItems` 按 schema group 查找当前光标所在的列表项，结果从内层到外层排列。\n- `getBlockIndentAtSelection` 将显式块缩进和列表层级换算为当前配置下的可视缩进。\n- `prepareBlockCommandFromList` 适用于 Slash Command 一类块命令：移除触发文本、退出列表并保留可视缩进，且整个操作可以一次撤销。\n\n缩进步长、最小值、最大值和默认值都可以通过 `ExtensionsKit` 配置：\n\n```ts\nimport { ExtensionsKit } from \"@halo-dev/richtext-editor\";\n\nExtensionsKit.configure({\n  indent: {\n    indentRange: 32,\n    minIndentLevel: 0,\n    maxIndentLevel: 320,\n    defaultIndentLevel: 0,\n  },\n});\n```\n\n未配置 `maxIndentLevel` 时，默认允许 10 级缩进，并会随 `indentRange` 自动换算最大值。\n","ui/packages/editor/docs/runtime-metadata.md":"# 编辑器扩展的运行期元数据\n\n`@halo-dev/richtext-editor` 允许 Tiptap 的 Node、Mark 和 Extension\n声明运行期组件元数据，用于描述最终 Editor 实例中的 schema、组件用法、结构关系、\n属性和示例。这些信息从 Editor 当前注册的扩展及最终 schema 中同步解析；AI Agent\n是目前的主要消费者，但元数据本身是对运行期组件能力的描述，不会改变或约束组件行为。\n\n## 声明新组件\n\n下面的数学公式节点说明了适用场景、属性和生成所需的外部能力：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MathBlock = Node.create({\n  name: \"mathBlock\",\n  group: \"block\",\n  atom: true,\n\n  addAttributes() {\n    return {\n      formula: {\n        default: \"\",\n      },\n    };\n  },\n\n  parseHTML() {\n    return [{ tag: 'div[data-type=\"math-block\"]' }];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    return [\"div\", { ...HTMLAttributes, \"data-type\": \"math-block\" }];\n  },\n\n  // 声明运行期元数据中的 AI 使用说明\n  addHaloEditorMetadata() {\n    return {\n      ai: {\n        description: \"A display mathematical formula.\",\n        exposure: \"available\",\n        useWhen: [\"Presenting a standalone mathematical expression.\"],\n        attributeGuidance: {\n          formula: {\n            description: \"Formula source written in LaTeX.\",\n            format: \"LaTeX\",\n            examples: [\"E = mc^2\"],\n          },\n        },\n        generation: {\n          mode: \"requires-capability\",\n          requiredCapabilities: [\"math-to-html\"],\n        },\n        examples: [\n          '<div data-type=\"math-block\" formula=\"E = mc^2\"></div>',\n        ],\n      },\n    };\n  },\n});\n```\n\nCapability 名称是开放字符串。Halo 只把它放入 Manifest，不负责查找或执行对应工具，由最终使用者扩展工具。\n\n## 扩展现有组件\n\n元数据与 Tiptap 的 `.extend()` 继承链一起自动合并。插件只需返回自己的局部补丁，\n不需要调用 `this.parent`，也不需要手动合并 Halo 已有说明：\n\n```ts\nimport { ExtensionCodeBlock } from \"@halo-dev/richtext-editor\";\n\nexport const HighlightedCodeBlock = ExtensionCodeBlock.extend({\n  addAttributes() {\n    return {\n      ...this.parent?.(),\n      highlightTheme: {\n        default: null,\n      },\n    };\n  },\n\n  addHaloEditorMetadata() {\n    return {\n      ai: {\n        attributeGuidance: {\n          highlightTheme: {\n            description: \"Syntax-highlighting theme.\",\n            allowedValues: [\"github-light\", \"github-dark\"],\n            omitWhen: [\"The editor default theme should be used.\"],\n          },\n        },\n      },\n    };\n  },\n});\n```\n\n最终 Manifest 会同时包含原 code block 的描述和 `highlightTheme`。\n\n## 为全局属性贡献说明\n\nPlain Extension 不会成为 Manifest 组件，但可向明确命名的 Node 或 Mark\n贡献元数据。这适合 `addGlobalAttributes()`：\n\n```ts\nimport { Extension } from \"@halo-dev/richtext-editor\";\n\nexport const Tone = Extension.create({\n  name: \"tone\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"paragraph\", \"heading\"],\n        attributes: {\n          tone: {\n            default: null,\n          },\n        },\n      },\n    ];\n  },\n\n  addHaloEditorMetadata() {\n    return {\n      contributions: [\n        {\n          targets: [\n            { kind: \"node\", name: \"paragraph\" },\n            { kind: \"node\", name: \"heading\" },\n          ],\n          metadata: {\n            ai: {\n              attributeGuidance: {\n                tone: {\n                  description: \"Writing tone for this block.\",\n                  allowedValues: [\"neutral\", \"friendly\", \"formal\"],\n                },\n              },\n            },\n          },\n        },\n      ],\n    };\n  },\n});\n```\n\n贡献只应用于最终 schema 中存在的目标。多个贡献冲突时，根据组件定义中的 priority 决定，\n较高者优先；priority 相同时，后注册者优先。\n\n## 组件自身的结构说明\n\n组件只声明自己的父级和数量关系，不跨组件声明子节点：\n\n```ts\naddHaloEditorMetadata() {\n  return {\n    ai: {\n      description: \"An optional caption belonging to a figure.\",\n    },\n    structure: {\n      allowedParents: [\"figure\"],\n      minPerParent: 0,\n      maxPerParent: 1,\n    },\n  };\n}\n```\n\n这表示当前组件只能位于 `figure` 下，在每个 `figure` 中可省略且最多出现一次。\n\n## 禁用与兼容\n\n- `ai: false` 明确建议 AI 不主动使用该组件，但组件的 schema 信息仍会出现在\n  Manifest 中供 AI 理解。\n- 声明抛错、字段无效、属性或父节点不存在时，生成器保留其他有效数据；\n  开发环境给出警告，生产环境不会因元数据阻止编辑器运行。\n- 未知字段会被丢弃。请勿把 system prompt、可执行回调或敏感信息放入元数据。\n\n每段文本最多 1,000 个字符；说明数组和 aliases 最多 10 项；\n`allowedValues` 和属性示例最多 32 项；组件 HTML 示例最多 3 个且每个不超过\n4 KiB；单组件 AI 元数据最多 16 KiB；单个 Manifest 的 AI 元数据最多\n128 KiB。\n\n## 读取运行期 Manifest\n\n在 Editor 创建完成后同步生成最终快照：\n\n```ts\nimport {\n  createHaloEditorManifest,\n  type HaloEditorManifest,\n  type VueEditor,\n} from \"@halo-dev/richtext-editor\";\n\nfunction editorManifest(editor: VueEditor): HaloEditorManifest {\n  return createHaloEditorManifest(editor);\n}\n```\n\nManifest 是当前 Editor 实例的运行期快照，包含全部 Node 和 Mark、规范化元数据、\n`version: 1` 以及稳定的 `signature`。AI 插件可以把它加入模型上下文，其他消费者\n也可以用它了解当前编辑器实际注册的组件；消费者自行决定是否根据其中的建议进行额外校验。\n","ui/packages/ui-plugin-bundler-kit/README.md":"# @halo-dev/ui-plugin-bundler-kit\n\nA frontend build toolkit for Halo UI plugin development, supporting both Vite and Rsbuild build systems.\n\n## Introduction\n\n`@halo-dev/ui-plugin-bundler-kit` is a frontend build configuration toolkit specifically designed for Halo UI plugin development. It provides pre-configured build settings to help developers quickly set up and build frontend interfaces for Halo plugins and theme-provided UI plugins.\n\n### Key Features\n\n- 🚀 **Ready to Use** - Provides pre-configured Vite and Rsbuild build settings\n- 📦 **Multi-Build Tool Support** - Supports both Vite and Rsbuild\n- 🔧 **Flexible Configuration** - Supports custom build configurations\n- 🎯 **Halo Optimized** - External dependencies and global variables optimized for Halo UI plugin development\n- 📁 **Smart Output** - Automatically selects output directory based on environment\n\n## Installation\n\n```bash\n# Using npm\nnpm install @halo-dev/ui-plugin-bundler-kit\n\n# Using yarn\nyarn add @halo-dev/ui-plugin-bundler-kit\n\n# Using pnpm\npnpm add @halo-dev/ui-plugin-bundler-kit\n```\n\n### Additional Dependencies\n\n**For Vite users**, install Vite and its Vue plugin:\n\n```bash\nnpm install vite @vitejs/plugin-vue\n```\n\n**For Rsbuild users**, install Rsbuild and its Vue plugin:\n\n```bash\nnpm install @rsbuild/core @rsbuild/plugin-vue\n```\n\nThe Vite and Rsbuild peer dependencies are optional. Install only the build tool and Vue plugin you use, and import its configuration helper from `@halo-dev/ui-plugin-bundler-kit/vite` or `@halo-dev/ui-plugin-bundler-kit/rsbuild`. The package root exports and the deprecated `HaloUIPluginBundlerKit` helper have been removed.\n\n## Usage\n\n### Vite Configuration\n\nCreate or update `vite.config.ts` file in your UI plugin project root:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  // provider defaults to \"plugin\"\n  vite: {\n    // Your custom Vite configuration\n    plugins: [\n      // Additional plugins (Vue plugin is already included)\n    ],\n    // Other configurations...\n  },\n});\n```\n\n> **Note**: Vue plugin is pre-configured. Pass its options through the top-level `vue` field instead of adding another `@vitejs/plugin-vue` instance.\n\n### Rsbuild Configuration\n\nCreate or update `rsbuild.config.ts` file in your UI plugin project root:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  // provider defaults to \"plugin\"\n  rsbuild: {\n    // Your custom Rsbuild configuration\n    plugins: [\n      // Additional plugins (Vue plugin is already included)\n    ],\n    // Other configurations...\n  },\n});\n```\n\n> **Note**: Vue plugin is pre-configured. Pass its options through the top-level `vue` field instead of adding another `@rsbuild/plugin-vue` instance.\n\n### Theme UI Plugin Configuration\n\nFor theme-provided Console/User Center UI plugins, place the frontend project under the theme's `ui-plugin/` directory:\n\n```text\ntheme-root/\n├── theme.yaml\n└── ui-plugin/\n    ├── package.json\n    ├── src/index.ts\n    └── vite.config.ts\n```\n\nVite:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  provider: \"theme\",\n  vite: {},\n});\n```\n\nRsbuild:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  provider: \"theme\",\n  rsbuild: {},\n});\n```\n\nThe theme provider reads `../theme.yaml`, outputs to `dist`, registers the module as `theme:{metadata.name}`, and configures assets for `/themes/{metadata.name}/ui-plugin/assets/`. Halo reads only `ui-plugin/dist/**` from the theme package.\n\n### Output Format and Halo Target\n\n`viteConfig` and `rsbuildConfig` accept the same format options:\n\n```typescript\nexport default viteConfig({\n  format: \"auto\", // \"auto\" | \"iife\" | \"esm\"\n  vite: {},\n});\n```\n\n`auto` is the default. It emits ESM when `spec.requires` is a stable Halo version or a simple `>=MAJOR.MINOR.PATCH` target whose minimum is Halo 2.26.0 or newer. Missing, wildcard, composite, or otherwise unsupported ranges produce a warning and keep the compatible IIFE output. This fallback is intentional: ESM is optional, and Halo continues loading old IIFE plugin and theme artifacts throughout Halo 2.x.\n\nUse an explicit target only when intentionally forcing ESM and a target cannot be derived:\n\n```typescript\nexport default viteConfig({\n  format: \"esm\",\n  targetHaloVersion: \"2.26.0\",\n  vite: {},\n});\n```\n\nAfter ESM is selected, successful builds generate `ui-plugin.json`. This filename is reserved for the bundler kit: do not create, copy, or emit another file with that name. A provider artifact without this file remains legacy, even if its `spec.requires` also supports Halo 2.26 or newer.\n\nESM entries must default-export the existing `PluginModule`. The generated manifest records one optional main stylesheet; CSS belonging to asynchronous chunks stays out of the manifest and loads on demand with its JavaScript chunk. Halo starts every provider-owned startup stylesheet and entry in parallel, then commits modules in provider order without reloading after each entry. Top-level module effects, timers, listeners, and arbitrary asynchronous effects are not transactional; a full page reload remains the lifecycle and recovery boundary after provider changes.\n\nThe default Vite and Rsbuild presets provide ESM output, provider-relative resource paths, content-hashed entry and startup-style filenames, content-hashed secondary resources, and Halo shared-runtime externals. The generated manifest records the actual startup filenames. Raw `vite` and `rsbuild` configuration is merged after these defaults and is not inspected, rejected, or rewritten. If it changes entry names, formats, public paths, externals, or caching behavior, the caller owns the resulting manifest consistency, Import Map compatibility, shared dependency identity, resource relocation, and cache safety. Compatible IIFE overrides retain their previous stable `main.js` behavior.\n\n### Shared Runtime Dependencies\n\nESM providers may import these package roots from Halo:\n\n- `vue`\n- `vue-router`\n- `pinia`\n- `axios`\n- `@formkit/vue`\n- `@formkit/core`\n- `@halo-dev/ui-shared`\n- `@halo-dev/components`\n- `@halo-dev/api-client`\n- `@halo-dev/richtext-editor`\n\nThe bundler discovers shared package root imports and reports the installed provider version beside the selected Halo host snapshot version when package metadata is available. A newer provider dependency emits a compatibility note, and a different major emits a stronger note, but version drift does not fail the build. Export usage, aliases, forks, and final bundler resolution are not inspected. Deep imports such as `vue/dist/vue.esm-bundler.js` still fail because Halo's Import Map exposes shared package roots only.\n\nVue, Vue Router, Pinia, and the FormKit Vue/Core graph share host identity. Other `@formkit/*` packages and VueUse stay provider-private. Non-shared dependencies are bundled by the default presets.\n\nThe shared `axios` import is the standard package module. Do not mutate its shared defaults or interceptors. For isolated clients, call `axios.create()`. `@halo-dev/api-client` exports Halo's separate authenticated `axiosInstance`; do not mutate that instance either.\n\n### Querying Other UI Providers\n\nUse the shared registration store instead of checking another provider's `window.PluginName` global:\n\n```typescript\nimport { stores } from \"@halo-dev/ui-shared\";\n\nconst uiPlugins = stores.uiPlugins();\n\nuiPlugins.isEnabled(\"plugin-search\");\nuiPlugins.isRegistered(\"plugin-search\");\nuiPlugins.get(\"plugin-search\");\n```\n\nThe reactive record contains only Halo-owned `name`, `type`, `version`, and `pending | registered | failed` status. `isEnabled` means the provider was discovered in the current descriptor; `isRegistered` becomes true after its current-page registration succeeds. Provider code treats this store as read-only and must not depend on another provider's evaluation order or module object.\n\n## Configuration Options\n\n### Vite Configuration Options\n\n```typescript\ninterface ViteUserConfig {\n  /**\n   * UI plugin provider type\n   * @default \"plugin\"\n   */\n  provider?: \"plugin\" | \"theme\";\n\n  /**\n   * Halo plugin or theme manifest file path\n   * @default \"../src/main/resources/plugin.yaml\" for plugins, \"../theme.yaml\" for themes\n   */\n  manifestPath?: string;\n\n  /** @default \"auto\" */\n  format?: \"auto\" | \"iife\" | \"esm\";\n\n  /** Required for explicit ESM when spec.requires has no derivable target. */\n  targetHaloVersion?: string;\n\n  /** Options for the built-in @vitejs/plugin-vue instance. */\n  vue?: VuePluginOptions;\n\n  /**\n   * Custom Vite configuration\n   */\n  vite: UserConfig | UserConfigFnObject;\n}\n```\n\n### Rsbuild Configuration Options\n\n```typescript\ninterface RsBuildUserConfig {\n  /**\n   * UI plugin provider type\n   * @default \"plugin\"\n   */\n  provider?: \"plugin\" | \"theme\";\n\n  /**\n   * Halo plugin or theme manifest file path\n   * @default \"../src/main/resources/plugin.yaml\" for plugins, \"../theme.yaml\" for themes\n   */\n  manifestPath?: string;\n\n  /** @default \"auto\" */\n  format?: \"auto\" | \"iife\" | \"esm\";\n\n  /** Required for explicit ESM when spec.requires has no derivable target. */\n  targetHaloVersion?: string;\n\n  /** Options for the built-in @rsbuild/plugin-vue instance. */\n  vue?: PluginVueOptions;\n\n  /**\n   * Custom Rsbuild configuration\n   */\n  rsbuild: RsbuildConfig | ((env: ConfigParams) => RsbuildConfig);\n}\n```\n\n## Advanced Configuration Examples\n\n### Customizing the Vue Compiler\n\nVite:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  vue: {\n    template: {\n      compilerOptions: {\n        isCustomElement: (tag) => tag === \"halo-app-card\",\n      },\n    },\n  },\n  vite: {},\n});\n```\n\nRsbuild:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  vue: {\n    vueLoaderOptions: {\n      compilerOptions: {\n        isCustomElement: (tag) => tag === \"halo-app-card\",\n      },\n    },\n  },\n  rsbuild: {},\n});\n```\n\nThe helper owns the Vue plugin instance. Keep `@vitejs/plugin-vue` and `@rsbuild/plugin-vue` out of the nested `plugins` array to avoid running the SFC transform twice.\n\n### Adding Path Aliases (Vite)\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\nimport path from \"path\";\n\nexport default viteConfig({\n  vite: {\n    resolve: {\n      alias: {\n        \"@\": path.resolve(__dirname, \"src\"),\n        \"@components\": path.resolve(__dirname, \"src/components\"),\n      },\n    },\n  },\n});\n```\n\n### Adding Path Aliases (Rsbuild)\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  rsbuild: {\n    source: {\n      alias: {\n        \"@\": \"./src\",\n        \"@components\": \"./src/components\",\n      },\n    },\n  },\n});\n```\n\n### Adding Additional Vite Plugins\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\nimport { defineConfig } from \"vite\";\nimport UnoCSS from \"unocss/vite\";\n\nexport default viteConfig({\n  vite: {\n    plugins: [\n      UnoCSS(), // Add UnoCSS plugin\n    ],\n  },\n});\n```\n\n### Adding Additional Rsbuild Plugins\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\nimport { pluginSass } from \"@rsbuild/plugin-sass\";\n\nexport default rsbuildConfig({\n  rsbuild: {\n    plugins: [\n      pluginSass(), // Add Sass plugin\n    ],\n  },\n});\n```\n\n### Custom Plugin Manifest Path\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  manifestPath: \"application/src/main/resources/plugin.yaml\", // Custom manifest file path\n  vite: {\n    // Other configurations...\n  },\n});\n```\n\n### Custom Theme Manifest Path\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  provider: \"theme\",\n  manifestPath: \"../custom-theme.yaml\",\n  vite: {\n    // Other configurations...\n  },\n});\n```\n\n## Development Scripts\n\nRecommended scripts to add to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"vite dev --mode=development --watch\",\n    \"build\": \"vite build\"\n  }\n}\n```\n\nFor Rsbuild:\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"rsbuild dev --env-mode=development --watch\",\n    \"build\": \"rsbuild build\"\n  }\n}\n```\n\n## Build Output\n\n> Relative to the UI plugin project root\n\nPlugin provider:\n\n- **Development**: `../build/resources/main/ui` or `../build/resources/main/console`\n- **Production**: `./build/dist`\n\nTheme provider:\n\n- **Development**: `dist`\n- **Production**: `dist`\n\nAn ESM output additionally contains the reserved, generated `ui-plugin.json` manifest and may contain content-hashed `chunks/` and `assets/`. The manifest contains `format`, the actual content-hashed `entry`, and an optional content-hashed `style`; asynchronous chunk CSS is not listed. Keep the complete output directory together. Halo serves these canonical paths through the existing plugin or theme static resource mapping without adding query cache keys, so imports back to the entry resolve to the same ESM module URL. Callers that replace the default content-hashed filenames accept the risk of stale resources under Halo's production static-resource cache. Legacy IIFE resources retain their version query behavior. The Halo 2.x compatibility `bundle.css` endpoint remains available for older callers, but now contains ordered `@import` rules pointing at those direct styles so relative asset URLs keep the correct provider base; it is not used by the new runtime.\n\n## Maintaining Halo Host Runtime Snapshots\n\nSnapshots capture the exact host version, statically importable root exports that are also present on the browser runtime global, bridge global, and identity category for each shared root. Synthetic namespace metadata such as `__esModule` is excluded when the browser global does not expose it. Snapshots do not claim an accepted provider version range. They are intentionally sparse: add a new immutable snapshot when Halo's shared runtime contract changes, not automatically for every Halo patch. A newer target selects the latest eligible older snapshot and emits a forward-compatibility warning; a target older than every packaged snapshot cannot build ESM.\n\nWhen the host dependency graph changes:\n\n1. Set `@halo-dev/ui-plugin-bundler-kit` to the Halo version represented by the snapshot.\n2. Run `pnpm --filter @halo-dev/ui-plugin-bundler-kit snapshot:generate` to derive the versioned output path and capture resolved host versions and exports.\n3. Run `pnpm --filter @halo-dev/ui-plugin-bundler-kit snapshot:check` and the compatibility fixtures before publishing.\n4. Preserve older snapshot files while supported provider artifacts may still target them.\n\nSnapshot generation is currently an explicit maintainer action; it is not coupled to every package build or CI job.\n\n## Requirements\n\n- **Node.js**: ^18.0.0 || >=20.0.0\n- **Peer Dependencies**:\n  - `@rsbuild/core`: ^1.0.0 || ^2.0.0 (when using Rsbuild)\n  - `@rsbuild/plugin-vue`: ^1.0.0 || ^2.0.0 (when using Rsbuild)\n  - `@vitejs/plugin-vue`: ^5.0.0 || ^6.0.0 (when using Vite)\n  - `vite`: ^6.0.0 || ^7.0.0 || ^8.0.0 (when using Vite)\n\n## Choosing Between Vite and Rsbuild\n\n**Vite is the recommended default for new UI plugins.** Halo 2.26.0 and later can load ESM plugin resources, so Vite builds can use dynamic imports and emit separate asynchronous chunks. Rsbuild was originally introduced to support code splitting while UI plugins were limited to legacy IIFE output; that is no longer a reason to prefer Rsbuild when targeting Halo 2.26.0 or later.\n\n### Use Vite by Default\n\n- **ESM code splitting** - Supports lazy loading and asynchronous chunks when the target Halo version selects ESM output.\n- **Broader ecosystem** - Provides wider plugin coverage and closer alignment with Vue ecosystem tooling.\n- **Strong build performance** - Recent Vite versions have substantially improved build performance, so build speed alone is usually not a reason to choose Rsbuild.\n- **Familiar configuration** - Keeps plugin projects aligned with the tooling commonly used by Vue applications.\n\n### Use Rsbuild When Needed\n\nChoose Rsbuild when at least one of these conditions applies:\n\n- The plugin must keep legacy IIFE output, such as when targeting Halo versions earlier than 2.26.0, but still needs code splitting.\n- The project already uses Rsbuild or depends on Rsbuild/Rspack-specific plugins and configuration.\n- Measurements on the actual project show a meaningful build-performance advantage.\n\nDo not choose a build system based only on plugin size. With ESM output, both Vite and Rsbuild support dynamic imports and code splitting. The following pattern works with either preset:\n\n```typescript\nimport { definePlugin } from \"@halo-dev/ui-shared\";\nimport { defineAsyncComponent } from \"vue\";\nimport { VLoading } from \"@halo-dev/components\";\n\nexport default definePlugin({\n  routes: [\n    {\n      parentName: \"Root\",\n      route: {\n        path: \"demo\",\n        name: \"DemoPage\",\n        // Lazy load heavy components\n        component: defineAsyncComponent({\n          loader: () => import(\"./views/DemoPage.vue\"),\n          loadingComponent: VLoading,\n        }),\n      },\n    },\n  ],\n  extensionPoints: {},\n});\n```\n\n### Summary\n\n| Consideration              | Vite                                       | Rsbuild                                         |\n| -------------------------- | ------------------------------------------ | ----------------------------------------------- |\n| Recommended use            | New plugins targeting Halo 2.26.0 or later | Existing Rsbuild projects or legacy IIFE builds |\n| ESM code splitting         | Supported                                  | Supported                                       |\n| Legacy IIFE code splitting | Not supported by the default preset        | Supported                                       |\n| Plugin ecosystem           | Broader Vite and Vue ecosystem             | Smaller, focused on Rsbuild and Rspack          |\n| Build performance          | Fast; recommended default                  | Fast; benchmark when performance is decisive    |\n\n## License\n\nGPL-3.0\n\n## Contributing\n\nIssues and Pull Requests are welcome! Please check our [Contributing Guide](https://github.com/halo-dev/halo/blob/main/CONTRIBUTING.md) for more information.\n\n## Related Links\n\n- [Halo Website](https://www.halo.run/)\n- [Halo Documentation](https://docs.halo.run/)\n- [GitHub Repository](https://github.com/halo-dev/halo)\n- [Plugin Development Guide](https://docs.halo.run/developer-guide/plugin/basics/ui/)\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Repository Guidelines\n\nHalo is a full-stack monorepo: Java 21 / Spring Boot WebFlux + R2DBC backend and a Vue 3 + TypeScript + TailwindCSS frontend. Work from the repo root and load **every relevant module guide** for the areas you touch.\n\n## Modules\n\n|       Module        |                           Path                            |\n|---------------------|-----------------------------------------------------------|\n| API library         | `api/` (extension model, contracts, security)             |\n| Backend application | `application/` (services, routers, migrations, packaging) |\n| Frontend            | `ui/` (console, user center, workspace packages)          |\n| Application BOM     | `platform/application/` (shared dependency constraints)   |\n| Plugin BOM          | `platform/plugin/` (plugin-facing constraints)            |\n\n## Quick Commands\n\nRun from the repo root:\n\n- `./gradlew build` — full backend build and tests.\n- `./gradlew spotlessApply` — format Java, Markdown, JSON, and properties.\n- `./gradlew :application:test` — backend tests.\n- `pnpm -C ui typecheck && pnpm -C ui lint` — frontend validation.\n- `pnpm -C ui test:unit` — frontend unit tests.\n- `./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen` — regenerate the UI client after contract changes.\n\n## Cross-Module Rules\n\n- Keep API contracts, backend handlers, and UI usage in sync; never hand-edit `ui/packages/api-client/src/` — regenerate it after contract changes.\n- Versions live in `gradle/libs.versions.toml`, `gradle.properties`, or `ui/package.json`; never hard-code them.\n- Branch naming: `feat/`, `fix/`, `improvement/`, `upgrade/`; PRs target `upstream` and follow `.github/pull_request_template.md`.\n- Stage specific files only; never `git add -A` (H2 artifacts and build outputs appear under `application/`).\n- Add tests for changed behavior; keep CI green.\n- Ask before public API changes, new dependencies, database migrations, security configuration, or CI workflow changes.\n- Never commit secrets or introduce blocking I/O into reactive flows.\n\n","isInternal":false,"tokens":473,"sizeBytes":2072},{"name":"AGENTS.md","path":"api/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/api/AGENTS.md","title":"api - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# API Library Guidelines\n\n`api/` is the public API library: the extension model, service contracts, and security abstractions consumed by the application and by plugins. Changes here affect downstream compatibility.\n\n## Scope\n\n- Source: `api/src/main/java/run/halo/app/` (core, extension, plugin, security, migration, theme, and related packages).\n- Tests: `api/src/test/`.\n\n## Commands\n\n- `./gradlew :api:test` — run API library tests.\n- `./gradlew spotlessApply` — format Java and Markdown.\n\n## Conventions\n\n- Java 21, 4-space indentation, formatted with Spotless.\n- The API is a compatibility surface: prefer additive changes, and flag any breaking signature or contract change for review before merging.\n- Extension definitions and schemas live in `application/src/main/resources/extensions/`; when you change them, update `application/` and `ui/` together.\n- Tests: JUnit 5, named `XxxTest` (unit) or `XxxIntegrationTest` (integration).\n\n## Boundaries\n\n- Public API changes in `api/` require explicit approval — treat them as affecting every plugin built against Halo.\n\n","isInternal":false,"tokens":246,"sizeBytes":1081},{"name":"AGENTS.md","path":"application/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/application/AGENTS.md","title":"application - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Backend Application Guidelines\n\n`application/` is the WebFlux + R2DBC backend: services, routers, security, migrations, and packaging. Everything here is reactive — never block threads.\n\n## Structure\n\n- `application/src/main/java/` — services, routers, endpoints, and configuration.\n- `application/src/main/resources/application*.yaml` — environment configuration.\n- `application/src/main/resources/db/migration/{h2,mariadb,mysql,postgresql}/` — per-dialect migrations.\n- `application/src/main/resources/extensions/` — extension schemas and role templates.\n- `application/src/test/` — backend tests.\n\n## Commands\n\n- `./gradlew :application:test` — run backend tests.\n- `./gradlew :application:bootRun` — start the backend dev server.\n- `./gradlew :application:copyUiDist` — embed the built UI into the JAR.\n- `./gradlew generateOpenApiDocs` — emit the OpenAPI spec used to regenerate the UI client.\n\n## Conventions\n\n- Keep reactive flows non-blocking; no blocking I/O in request paths.\n- Database migrations are versioned per dialect — never auto-generate them, and review them with the `application/` guide in mind.\n- Tests: JUnit 5 with Spring Boot test support; name unit tests `XxxTest` and integration tests `XxxIntegrationTest`.\n- After DTO, route, or schema changes, regenerate the UI client (`./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen`) and update `ui/` usage.\n\n## Boundaries\n\n- Ask before adding database migrations, changing security configuration, or introducing new dependencies.\n\n","isInternal":false,"tokens":347,"sizeBytes":1534},{"name":"AGENTS.md","path":"ui/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/ui/AGENTS.md","title":"ui - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Frontend Guidelines\n\n`ui/` is the Vue 3 + TypeScript + TailwindCSS frontend, managed with pnpm workspaces.\n\n## Structure\n\n- `ui/console-src/` — console application (entry: `console.html`, alias `@console`).\n- `ui/uc-src/` — user center application (entry: `uc.html`, alias `@uc`).\n- `ui/src/` — shared code: build-time Vite helpers (`src/vite/`), locales, assets, and shared views (alias `@`); not an application entry point.\n- `ui/packages/` — shared workspace packages (`@halo-dev/api-client`, `@halo-dev/components`, `@halo-dev/console-shared`, `@halo-dev/editor`, `@halo-dev/shared`, `@halo-dev/ui-plugin-bundler-kit`).\n- Unit tests: `*.spec.ts` files, usually under `__tests__/` next to the code they cover.\n\n## Commands\n\nRun from the repo root:\n\n- `pnpm -C ui install` / `pnpm -C ui dev` — install dependencies / run the dev server.\n- `pnpm -C ui build` — production build.\n- `pnpm -C ui typecheck` — `vue-tsc` type checking.\n- `pnpm -C ui lint` — ESLint (fails on any warning).\n- `pnpm -C ui format` / `pnpm -C ui format:check` — Prettier formatting.\n- `pnpm -C ui test:unit` — Vitest unit tests.\n- `./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen` — regenerate `ui/packages/api-client` after backend contract changes.\n\n## Conventions\n\n- Follow existing component and composable patterns; match surrounding code rather than introducing new stylistic islands.\n- Never hand-edit `ui/packages/api-client/src/` — it is generated; regenerate it instead.\n- Keep application dependency versions in `ui/package.json`. For dependencies shared by workspace packages, define versions in the catalog in `ui/pnpm-workspace.yaml` and reference them with `catalog:`; never duplicate version declarations across packages.\n- Payload or UX changes usually ship together with the backend change that defines the contract.\n\n## Boundaries\n\n- New npm dependencies require approval.\n","isInternal":false,"tokens":487,"sizeBytes":1904},{"name":"CLAUDE.md","path":"api/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/api/CLAUDE.md","title":"api - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"@AGENTS.md\n","isInternal":false,"tokens":5,"sizeBytes":11},{"name":"CLAUDE.md","path":"application/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/application/CLAUDE.md","title":"application - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"@AGENTS.md\n","isInternal":false,"tokens":5,"sizeBytes":11},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"@AGENTS.md\n","isInternal":false,"tokens":5,"sizeBytes":11},{"name":"CLAUDE.md","path":"ui/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/ui/CLAUDE.md","title":"ui - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"@AGENTS.md\n","isInternal":false,"tokens":5,"sizeBytes":11},{"name":"SKILL.md","path":".claude/skills/gradle-dependency-updates/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/skills/gradle-dependency-updates/SKILL.md","title":"gradle-dependency-updates","category":"anthropic-skill","format":"markdown","content":"---\nname: gradle-dependency-updates\ndescription: >-\n  Upgrade outdated Gradle dependencies in this project. Use this skill whenever\n  the user asks to check for dependency updates, show what's outdated, or\n  upgrade/bump/update Gradle dependencies, libraries, or plugins — including\n  mentions of \"dependencyUpdates\", the versions plugin, or \"outdated\n  dependencies\". Backend only (api/application/platform); frontend dependencies\n  (ui/, pnpm) are out of scope.\n---\n\n# Gradle Dependency Updates\n\nThis project manages backend dependencies through a Gradle version catalog\n(`gradle/libs.versions.toml`) plus the Spring Boot BOM. The ben-manes Versions\nplugin is already applied to `:api` and `:application`.\n\n## 1. Find what's outdated\n\n```bash\n./gradlew dependencyUpdates -DoutputFormatter=plain,json --console=plain\n```\n\nRead the reports at `api/build/dependencyUpdates/report.{txt,json}` and\n`application/build/dependencyUpdates/report.{txt,json}`; merge and dedupe\nacross the two modules.\n\nThe plugin's default revision includes stable releases, so \"later milestone\nversions\" does not mean pre-releases — only versions with a qualifier\n(alpha/beta/rc) are pre-releases.\n\n## 2. Decide what can be bumped\n\n- **Catalog library** — declared in `[libraries]` of `gradle/libs.versions.toml`\n  → bumpable in the catalog.\n- **Gradle plugin** — declared in `[plugins]` → bumpable in the catalog.\n- **BOM-managed** — pinned by the Spring Boot BOM in `platform/application`\n  (Spring Framework, r2dbc drivers, postgresql, byte-buddy, jspecify,\n  junit-platform-launcher, ...) → never bump individually; they arrive with the\n  next Spring Boot BOM bump.\n- **Gradle wrapper** — `gradle/wrapper/gradle-wrapper.properties`.\n\n## 3. Project constraints (do not cross)\n\n- **thymeleaf**: the `runtimeOnly ':thymeleaf:...'` flat-dir jars in\n  `application/build.gradle` are a pinned workaround (halo-dev/halo#7289) —\n  never touch them.\n- **lombok**: keep aligned with the `io.freefair.lombok` plugin; do not add a\n  `lombok { version = ... }` override.\n- **ben-manes Versions plugin itself**: stay on 0.54.0 — 0.59.0 fails to apply\n  with a classloader error.\n- **Pre-releases** (e.g. tika-core 4.0.0-beta-1): skip unless the user asks.\n- Versions live only in `gradle/libs.versions.toml`, `gradle.properties`, or\n  `ui/package.json` — never hard-code them (AGENTS.md).\n\n## 4. Show the update plan\n\nGroup by category, current → latest with the semver delta (major/minor/patch),\nstate where each bump lands, and list excluded items with reasons.\n\n## 5. Apply and verify\n\nEdit `gradle/libs.versions.toml` — one line per bump — only after the user\napproves the plan, then confirm the build:\n\n```bash\n./gradlew build\n```\n\nRe-run `dependencyUpdates` and check that the bumped items no longer appear as\noutdated; what remains should be exactly the out-of-scope set.\n","frontmatter":{"name":"gradle-dependency-updates","description":"Upgrade outdated Gradle dependencies in this project. Use this skill whenever the user asks to check for dependency updates, show what's outdated, or upgrade/bump/update Gradle dependencies, libraries, or plugins — including mentions of \"dependencyUpdates\", the versions plugin, or \"outdated dependencies\". Backend only (api/application/platform); frontend dependencies (ui/, pnpm) are out of scope."},"isInternal":false,"tokens":732,"sizeBytes":2875},{"name":"SKILL.md","path":".claude/skills/openspec-apply-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/skills/openspec-apply-change/SKILL.md","title":"openspec-apply-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n","frontmatter":{"name":"openspec-apply-change","description":"Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1276,"sizeBytes":5465},{"name":"SKILL.md","path":".claude/skills/openspec-archive-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/skills/openspec-archive-change/SKILL.md","title":"openspec-archive-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n","frontmatter":{"name":"openspec-archive-change","description":"Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1147,"sizeBytes":4965},{"name":"SKILL.md","path":".claude/skills/openspec-explore/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/skills/openspec-explore/SKILL.md","title":"openspec-explore","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n","frontmatter":{"name":"openspec-explore","description":"Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":2327,"sizeBytes":11428},{"name":"SKILL.md","path":".claude/skills/openspec-propose/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/skills/openspec-propose/SKILL.md","title":"openspec-propose","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n","frontmatter":{"name":"openspec-propose","description":"Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1231,"sizeBytes":5465},{"name":"SKILL.md","path":".claude/skills/openspec-sync-specs/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/skills/openspec-sync-specs/SKILL.md","title":"openspec-sync-specs","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n","frontmatter":{"name":"openspec-sync-specs","description":"Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1184,"sizeBytes":5187},{"name":"SKILL.md","path":".claude/skills/openspec-update-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/skills/openspec-update-change/SKILL.md","title":"openspec-update-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n","frontmatter":{"name":"openspec-update-change","description":"Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1305,"sizeBytes":5791},{"name":"SKILL.md","path":".codex/skills/openspec-apply-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.codex/skills/openspec-apply-change/SKILL.md","title":"openspec-apply-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n","frontmatter":{"name":"openspec-apply-change","description":"Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1276,"sizeBytes":5465},{"name":"SKILL.md","path":".codex/skills/openspec-archive-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.codex/skills/openspec-archive-change/SKILL.md","title":"openspec-archive-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n","frontmatter":{"name":"openspec-archive-change","description":"Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1147,"sizeBytes":4965},{"name":"SKILL.md","path":".codex/skills/openspec-explore/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.codex/skills/openspec-explore/SKILL.md","title":"openspec-explore","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n","frontmatter":{"name":"openspec-explore","description":"Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":2327,"sizeBytes":11428},{"name":"SKILL.md","path":".codex/skills/openspec-propose/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.codex/skills/openspec-propose/SKILL.md","title":"openspec-propose","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n","frontmatter":{"name":"openspec-propose","description":"Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1231,"sizeBytes":5465},{"name":"SKILL.md","path":".codex/skills/openspec-sync-specs/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.codex/skills/openspec-sync-specs/SKILL.md","title":"openspec-sync-specs","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n","frontmatter":{"name":"openspec-sync-specs","description":"Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1184,"sizeBytes":5187},{"name":"SKILL.md","path":".codex/skills/openspec-update-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.codex/skills/openspec-update-change/SKILL.md","title":"openspec-update-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n","frontmatter":{"name":"openspec-update-change","description":"Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1305,"sizeBytes":5791},{"name":"SKILL.md","path":".github/skills/openspec-apply-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.github/skills/openspec-apply-change/SKILL.md","title":"openspec-apply-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n","frontmatter":{"name":"openspec-apply-change","description":"Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1276,"sizeBytes":5465},{"name":"SKILL.md","path":".github/skills/openspec-archive-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.github/skills/openspec-archive-change/SKILL.md","title":"openspec-archive-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n","frontmatter":{"name":"openspec-archive-change","description":"Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1147,"sizeBytes":4965},{"name":"SKILL.md","path":".github/skills/openspec-explore/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.github/skills/openspec-explore/SKILL.md","title":"openspec-explore","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n","frontmatter":{"name":"openspec-explore","description":"Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":2327,"sizeBytes":11428},{"name":"SKILL.md","path":".github/skills/openspec-propose/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.github/skills/openspec-propose/SKILL.md","title":"openspec-propose","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n","frontmatter":{"name":"openspec-propose","description":"Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1231,"sizeBytes":5465},{"name":"SKILL.md","path":".github/skills/openspec-sync-specs/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.github/skills/openspec-sync-specs/SKILL.md","title":"openspec-sync-specs","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n","frontmatter":{"name":"openspec-sync-specs","description":"Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1184,"sizeBytes":5187},{"name":"SKILL.md","path":".github/skills/openspec-update-change/SKILL.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.github/skills/openspec-update-change/SKILL.md","title":"openspec-update-change","category":"anthropic-skill","format":"markdown","content":"---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n","frontmatter":{"name":"openspec-update-change","description":"Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.","allowed-tools":"Bash(openspec:*)","license":"MIT","compatibility":"Requires openspec CLI.","metadata":{"author":"openspec","version":"1.0","generatedBy":"1.6.0"},"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1305,"sizeBytes":5791},{"name":"extension.md","path":"ui/packages/editor/docs/extension.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/ui/packages/editor/docs/extension.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# 扩展说明\n\n本文档介绍如何对编辑器的功能进行扩展，包括但不限于扩展工具栏、悬浮工具栏、Slash Command、拖拽功能等。各扩展区域参考下图：\n\n![编辑器扩展说明](extension.png)\n\n目前支持的所有扩展类型 [ExtensionOptions](../packages/editor/src/types/index.ts) 如下所示：\n\n```ts\nexport interface ExtensionOptions {\n  // 顶部工具栏扩展\n  getToolbarItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => ToolbarItemType | ToolbarItemType[];\n\n  // Slash Command 扩展\n  getCommandMenuItems?: () => CommandMenuItemType | CommandMenuItemType[];\n\n  // 悬浮菜单扩展\n  getBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenuType;\n\n  // 工具箱扩展\n  getToolboxItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => ToolboxItemType | ToolboxItemType[];\n\n  // 拖拽菜单扩展\n  getDraggableMenuItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => DragButtonType | DragButtonType[];\n}\n```\n\n> 对于 Tiptap 本身的扩展方式可以参考 <https://tiptap.dev/api/introduction>\n\n## 1. 顶部工具栏扩展\n\n编辑器顶部功能区域内容的扩展，通常用于增加用户常用操作，例如文本加粗、变更颜色等。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/16> 中，我们实现了对顶部工具栏的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolbarItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolbarItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getToolbarItems` 即为对顶部工具栏的扩展。其返回类型为：\n\n```ts\n// 顶部工具栏扩展\ngetToolbarItems?: ({\n  editor,\n}: {\n  editor: Editor;\n}) => ToolbarItemType | ToolbarItemType[];\n\n// 工具栏\nexport interface ToolbarItemType {\n  priority: number;\n  component: Component;\n  props: {\n    editor: Editor;\n    isActive: boolean;\n    disabled?: boolean;\n    icon?: Component;\n    title?: string;\n    shortcutId?: string;\n    shortcutIds?: string[];\n    action?: () => void;\n  };\n  children?: ToolbarItemType[];\n}\n```\n\n如下为 [`Bold`](../packages/editor/src/extensions/bold/index.ts) 扩展中对于 `getToolbarItems` 的扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n    getToolbarItems({ editor }: { editor: Editor }) {\n      return {\n        priority: 40,\n        component: markRaw(ToolbarItem),\n        props: {\n          editor,\n          isActive: editor.isActive(\"bold\"),\n          icon: markRaw(MdiFormatBold),\n          title: i18n.global.t(\"editor.common.bold\"),\n          action: () => editor.chain().focus().toggleBold().run(),\n        },\n      };\n    },\n  };\n},\n```\n\n## 2. 快捷键与提示信息\n\nHalo 在 Tiptap 的 `addKeyboardShortcuts` 基础上提供了快捷键描述注册表。第三方扩展仍然只需要实现一个 `addKeyboardShortcuts`，同时即可获得以下能力：\n\n- 执行 Tiptap 快捷键命令；\n- 在“键盘快捷键”侧边栏中展示操作说明；\n- 通过同一个 `shortcutId` 在工具栏或悬浮菜单的 tooltip 中展示快捷键；\n- 根据当前操作系统将 `Mod`、`Alt` 等按键格式化为对应的展示形式。\n\n### 2.1 注册快捷键并关联工具栏\n\n使用 `defineHaloKeyboardShortcuts` 定义快捷键，再将相同的 `id` 传给工具栏组件的 `shortcutId`：\n\n```ts\nimport {\n  defineHaloKeyboardShortcuts,\n  Extension,\n  ToolbarItem,\n  type Editor,\n  type ExtensionOptions,\n} from \"@halo-dev/richtext-editor\";\nimport { markRaw } from \"vue\";\nimport MyIcon from \"./MyIcon.vue\";\n\nconst shortcutId = \"plugin.example.insertGreeting\";\n\nfunction insertGreeting(editor: Editor) {\n  return editor.chain().focus().insertContent(\"Hello Halo\").run();\n}\n\nexport const ExtensionExample = Extension.create<ExtensionOptions>({\n  name: \"exampleShortcut\",\n\n  addKeyboardShortcuts() {\n    return defineHaloKeyboardShortcuts(this, [\n      {\n        id: shortcutId,\n        keys: [\"Mod-Alt-g\"],\n        label: \"插入问候语\",\n        category: \"general\",\n        priority: 100,\n        command: () => insertGreeting(this.editor),\n      },\n    ]);\n  },\n\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolbarItems({ editor }: { editor: Editor }) {\n        return {\n          priority: 100,\n          component: markRaw(ToolbarItem),\n          props: {\n            editor,\n            isActive: false,\n            icon: markRaw(MyIcon),\n            title: \"插入问候语\",\n            shortcutId,\n            action: () => insertGreeting(editor),\n          },\n        };\n      },\n    };\n  },\n});\n```\n\n`ToolbarItem`、`ToolbarSubItem` 和 `BubbleItem` 都支持 `shortcutId`。`ToolbarItem` 还支持 `shortcutIds`，适用于一个按钮对应多个操作的情况，例如同时展示“增大字号”和“减小字号”。tooltip 会展示每个快捷键定义中的第一组按键，快捷键侧边栏则会展示 `keys` 中的全部可选按键。\n\n默认编辑器已经通过 `ExtensionsKit` 内置 `ExtensionKeyboardShortcuts`，插件通过 `default:editor:extension:create` 扩展点注册时无需重复添加。自行创建编辑器实例时，应使用 `ExtensionsKit`，或显式加入 `ExtensionKeyboardShortcuts`。\n\n### 2.2 描述字段\n\n`defineHaloKeyboardShortcuts` 接收的每一项都是一个 `HaloKeyboardShortcutDefinition`：\n\n| 字段 | 必填 | 说明 |\n| --- | --- | --- |\n| `id` | 是 | 编辑器内稳定且唯一的标识符，用于关联 tooltip。插件应使用包含插件标识的命名空间，例如 `plugin.example.insertGreeting`。 |\n| `keys` | 是 | Tiptap 格式的按键组合。第一项是 tooltip 展示的主快捷键，全部按键都会展示在快捷键侧边栏中。 |\n| `label` | 是 | 用户可见的操作名称，可以是字符串或返回字符串的函数。 |\n| `category` | 是 | 快捷键侧边栏分组：`general`、`formatting`、`structure` 或 `navigation`。 |\n| `command` | 视情况 | 新增快捷键时必须提供。扩展已有 Tiptap 快捷键时可以省略，此时复用父扩展中相同按键的命令。 |\n| `description` | 否 | 操作的补充说明，可以是字符串或返回字符串的函数。 |\n| `priority` | 否 | 在快捷键侧边栏同一分组中的排序值，数值越小越靠前，默认为 `100`。 |\n| `discoverable` | 否 | 是否出现在快捷键侧边栏中，默认为 `true`。即使设为 `false`，显式绑定了 `shortcutId` 的 tooltip 仍可展示。 |\n| `visible` | 否 | 根据当前编辑器状态决定是否出现在快捷键侧边栏中。 |\n\n按键名称遵循 [Tiptap 快捷键格式](https://tiptap.dev/docs/editor/core-concepts/keyboard-shortcuts)。建议使用 `Mod` 表示 macOS 的 `Command` 和 Windows/Linux 的 `Control`，例如 `Mod-b`。命令处理成功时应返回 `true`，这样 ProseMirror 会阻止浏览器继续执行同一按键的默认行为；未处理时应返回 `false`。\n\n如果扩展继承的 Tiptap 扩展已经实现了相同按键，可以只补充 Halo 的描述信息，不需要重新实现命令：\n\n```ts\naddKeyboardShortcuts() {\n  return defineHaloKeyboardShortcuts(this, [\n    {\n      id: \"plugin.example.toggleFeature\",\n      keys: [\"Mod-b\"],\n      label: \"切换示例功能\",\n      category: \"formatting\",\n    },\n  ]);\n},\n```\n\n只有父扩展确实定义了 `keys` 中对应的按键时才能省略 `command`。开发环境会对缺少实际命令的定义输出警告，并且不会注册这条描述。\n\n### 2.3 自定义组件中的 tooltip\n\n完全自定义工具栏组件时，可以通过 `useHaloKeyboardShortcut` 响应式读取注册表，再使用 `KeyboardShortcutTooltip` 保持与内置工具栏一致的视觉和无障碍信息：\n\n```vue\n<script setup lang=\"ts\">\nimport {\n  KeyboardShortcutTooltip,\n  useHaloKeyboardShortcut,\n  type Editor,\n} from \"@halo-dev/richtext-editor\";\n\nconst props = defineProps<{\n  editor: Editor;\n  shortcutId: string;\n  title: string;\n}>();\n\nconst shortcut = useHaloKeyboardShortcut(props.editor, () => props.shortcutId);\n</script>\n\n<template>\n  <KeyboardShortcutTooltip\n    v-slot=\"tooltipProps\"\n    :title=\"title\"\n    :shortcut=\"shortcut?.keys[0]\"\n  >\n    <button :aria-label=\"tooltipProps.ariaLabel\" type=\"button\">\n      {{ title }}\n    </button>\n  </KeyboardShortcutTooltip>\n</template>\n```\n\n一个组件需要读取多条快捷键时，可以使用 `useHaloKeyboardShortcuts(editor, () => shortcutIds)`。这两个 composable 必须在 Vue 组件的 `setup` 阶段调用，以便组件卸载时自动取消注册表订阅。\n\n### 2.4 命名与冲突规则\n\n- `id` 应包含插件标识，避免覆盖其他扩展注册的描述；快捷键注册表不会自动为重复 ID 添加命名空间。\n- 只注册产品中真实可执行的快捷键，不要为了填满快捷键侧边栏而自行创造按键组合。\n- 添加按键前应检查 Halo 默认快捷键、Tiptap 默认快捷键以及浏览器常用快捷键。确实需要覆盖浏览器默认行为时，命令必须在成功处理后返回 `true`。\n- `label` 和 `description` 应面向用户描述操作，不要使用内部命令名或扩展名。\n\n## 3. 工具箱扩展\n\n编辑器工具箱区域的扩展，可用于增加编辑器附属操作，例如插入表格，插入第三方组件等功能。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/27> 中，我们实现了对编辑器工具箱区域的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolboxItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolboxItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getToolboxItems` 即为对工具箱的扩展。其返回类型为：\n\n```ts\n// 工具箱扩展\ngetToolboxItems?: ({\n  editor,\n}: {\n  editor: Editor;\n}) => ToolboxItemType | ToolboxItemType[];\n\nexport interface ToolboxItemType {\n  priority: number;\n  component: Component;\n  props: {\n    editor: Editor;\n    icon?: Component;\n    title?: string;\n    description?: string;\n    action?: () => void;\n  };\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getToolboxItems` 工具箱的扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n    getToolboxItems({ editor }: { editor: Editor }) {\n      return {\n        priority: 15,\n        component: markRaw(ToolboxItem),\n        props: {\n          editor,\n          icon: markRaw(MdiTablePlus),\n          title: i18n.global.t(\"editor.menus.table.add\"),\n          action: () =>\n            editor\n              .chain()\n              .focus()\n              .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n              .run(),\n        },\n      };\n    },\n  }\n}\n```\n\n## 4. Slash Command 扩展\n\nSlash Command （斜杠命令）的扩展，可用于在当前行快捷执行功能操作，例如转换当前行为标题、在当前行添加代码块等功能。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/16> 中，我们实现了对 Slash Command 指令的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getCommandMenuItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getCommandMenuItems() {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getCommandMenuItems` 即为对工具箱的扩展。其返回类型为：\n\n```ts\n// Slash Command 扩展\ngetCommandMenuItems?: () => CommandMenuItemType | CommandMenuItemType[];\n\nexport interface CommandMenuItemType {\n  priority: number;\n  icon: Component;\n  title: string;\n  keywords: string[];\n  command: ({ editor, range }: { editor: Editor; range: Range }) => void;\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getCommandMenuItems` 的扩展示例：\n\n```ts\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getCommandMenuItems() {\n        return {\n          priority: 120,\n          icon: markRaw(MdiTable),\n          title: \"editor.extensions.commands_menu.table\",\n          keywords: [\"table\", \"biaoge\"],\n          command: ({ editor, range }: { editor: Editor; range: Range }) => {\n            editor\n              .chain()\n              .focus()\n              .deleteRange(range)\n              .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n              .run();\n          },\n        };\n      },\n    }\n  }\n```\n\n## 5. 悬浮菜单扩展\n\n编辑器悬浮菜单的扩展。可用于支持目标元素组件的功能扩展及操作简化。例如 `Table` 扩展中的添加下一列、添加上一列等操作。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/38> 中，我们重构了对编辑器悬浮区域的扩展，如果需要对某个块进行支持，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getBubbleMenu` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getBubbleMenu({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getBubbleMenu` 即为对悬浮菜单的扩展。其返回类型为：\n\n```ts\n// 悬浮菜单扩展\ngetBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenuType;\n\ninterface BubbleMenuProps {\n  pluginKey?: string;                                             // 悬浮菜单插件 Key，建议命名方式 xxxBubbleMenu\n  editor?: Editor;\n  shouldShow: (props: {                                           // 悬浮菜单显示的条件\n    editor: Editor;\n    state: EditorState;\n    node?: HTMLElement;\n    view?: EditorView;\n    oldState?: EditorState;\n    from?: number;\n    to?: number;\n  }) => boolean;\n  tippyOptions?: Record<string, unknown>;                          // 可自由定制悬浮菜单所用的 tippy 组件的选项\n  getRenderContainer?: (node: HTMLElement) => HTMLElement;         // 悬浮菜单所基准的 DOM\n  defaultAnimation?: boolean;                                      // 是否启用默认动画。默认为 true\n}\n\n// 悬浮菜单\nexport interface NodeBubbleMenuType extends BubbleMenuProps {\n  component?: Component;                                           // 不使用默认的样式，与 items 二选一\n  items?: BubbleItemType[];                                       // 悬浮菜单子项，使用默认的形式进行，与 items 二选一\n  extendsKey?: string | PluginKey;                                 // 用于扩展已有悬浮菜单的 key，如果未提供，则会被视为一个新的悬浮菜单\n}\n\n// 悬浮菜单子项\nexport interface BubbleItemType {\n  priority: number;                                                // 优先级，数字越小优先级越大，越靠前\n  component?: Component;                                           // 完全自定义子项样式\n  key?: string;                                                    // 子项的唯一标识，通常用于扩展悬浮菜单时仅保留唯一的子项。\n  props?: {                                                        // 子项属性，可选。同时支持传入自定义属性\n    isActive?: ({ editor }: { editor: Editor }) => boolean;         // 当前功能是否已经处于活动状态\n    visible?: ({ editor }: { editor: Editor }) => boolean;         // 是否显示当前子项\n    icon?: Component;                                              // 图标\n    iconStyle?: string;                                            // 图标自定义样式\n    title?: string;                                                // 标题\n    action?: ({ editor }: { editor: Editor }) => Component | void; // 点击子项后的操作，如果返回 Component，则会将其包含在下拉框中。\n  } & Record<string, unknown>;\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getBubbleMenu` 悬浮菜单的部分扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n      getBubbleMenu({ editor }) {\n        return {\n          pluginKey: \"tableBubbleMenu\",\n          shouldShow: ({ state }: { state: EditorState }): boolean => {\n            return isActive(state, Table.name);\n          },\n          getRenderContainer(node) {\n            let container = node;\n            if (container.nodeName === \"#text\") {\n              container = node.parentElement as HTMLElement;\n            }\n            while (\n              container &&\n              container.classList &&\n              !container.classList.contains(\"tableWrapper\")\n            ) {\n              container = container.parentElement as HTMLElement;\n            }\n            return container;\n          },\n          tippyOptions: {\n            offset: [26, 0],\n          },\n          items: [\n            {\n              priority: 10,\n              props: {\n                icon: markRaw(MdiTableColumnPlusBefore),\n                title: i18n.global.t(\"editor.menus.table.add_column_before\"),\n                action: () => editor.chain().focus().addColumnBefore().run(),\n              },\n            },\n          ]\n        }\n      }\n  }\n}\n```\n\n## 6. 拖拽菜单扩展\n\n拖拽菜单扩展主要用于拖拽的菜单功能扩展，例如转换为、复制、剪切、删除等操作。\n\n在 <https://github.com/halo-dev/halo/pull/7861> 中，我们重构了对编辑器拖拽区域的扩展，并且支持了对拖拽菜单的扩展。如果需要对拖拽菜单进行扩展，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getDraggableMenuItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n同时，为了支持不同扩展对同一菜单项的扩展，我们提供了 `extendsKey` 属性，用于指定扩展目标菜单项的唯一标识。只需将 `extendsKey` 设置为已有的菜单项的 `key`，即可扩展该菜单项。可扩展已有菜单项的 `visible`、`isActive`、`disabled`、`action` 方法以及 `children.items` 属性，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return {\n          extendsKey: CONVERT_TO_KEY,\n          // 当任意扩展目标菜单项的 visible 方法返回 false 时，当前菜单项不会显示。返回 true 则会继续执行后续的扩展实现。\n          visible: ({ editor }) => {\n            if (isActive(editor.state, \"table\")) {\n              return false;\n            }\n            return true;\n          },\n        };\n      },\n    };\n  },\n};\n```\n\n拖拽菜单最多支持两级菜单嵌套， 如果想扩展已有的一级菜单，为其二级菜单增加内容，则需要同时设置 `extendsKey` 和 `children.items` 属性。如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return {\n          extendsKey: CONVERT_TO_KEY,\n          children: {\n            items: [\n              {\n                priority: 10,\n                icon: markRaw(MdiFormatParagraph),\n                title: i18n.global.t(\"editor.common.heading.paragraph\"),\n                action: ({ editor }: { editor: Editor }) =>\n                  editor.chain().focus().setParagraph().run(),\n              },\n            ],\n          },\n        }\n      },\n    };\n  },\n}\n```\n\n默认情况下，将会追加 `items`，若想覆盖，则需要设置子菜单的 `key` 属性，将会覆盖原有的子菜单项。\n\n下面为 `getDraggableMenuItems` 的返回类型：\n\n```ts\n\n// 拖拽菜单扩展\ngetDraggableMenuItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => DragButtonType | DragButtonType[];\n\n// 拖拽菜单项目属性\nexport interface DragButtonItemProps {\n  extendsKey?: string;                                    // 扩展目标菜单项的唯一标识，如果提供了该属性，则视为扩展目标菜单项。\n  key?: string;                                           // 唯一标识，如果同级菜单项设置了同样的 key，则会被合并为一个菜单项。\n  priority?: number;                                      // 优先级，数字越小优先级越大，越靠前\n  title?: string | (() => string);                        // 标题\n  icon?: Component;                                       // 图标\n  action?: ({                                             // 点击菜单后的操作，如果返回 Component，则会将其包含在子菜单中。\n                                                          // 可以通过调用 close 方法可以在操作完成后关闭拖拽菜单，或者当返回为 true 或 undefined 时，会自动关闭拖拽菜单，如果返回 false，则不会关闭拖拽菜单。\n                                                          // 多个扩展实现时，则按照顺序执行，并在返回非 undefined 值时停止执行。\n    editor,\n    node,\n    pos,\n    close,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n    close: () => void;\n  }) => Component | boolean | void | Promise<Component | boolean | void>;\n  iconStyle?: string;                                       // 图标自定义样式\n  class?: string;                                           // 自定义样式\n  visible?: ({                                              // 是否显示当前菜单项，默认为 true，多个扩展实现时，以 AND 逻辑判断，即所有扩展返回 true 时，当前菜单项才会显示。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  isActive?: ({                                             // 当前菜单项是否处于活动状态，默认为 false，多个扩展实现时，以 OR 逻辑判断，即只要有一个扩展返回 true，则当前菜单项处于活动状态。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  disabled?: ({                                                // 是否禁用当前菜单项，默认为 false，多个扩展实现时，以 OR 逻辑判断，即只要有一个扩展返回 true，则当前菜单项会被禁用。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  keyboard?: string;                                            // 快捷键，遵循 https://tiptap.dev/docs/editor/core-concepts/keyboard-shortcuts\n  component?: Component;                                        // 自定义组件，如果提供了该属性，则不会显示默认的菜单项，而是会显示自定义组件，并且将所有 props 传递给自定义组件。\n  [key: string]: any;                                           // 其他自定义属性，将会传递给自定义组件。\n}\n\n// 一级菜单项\nexport interface DragButtonType extends DragButtonItemProps {\n  children?: {                                                    // 子菜单项，如果提供了该属性，则视为扩展目标菜单项的二级菜单。\n    component?: Component;                                        // 自定义组件，如果提供了该属性，则不会显示默认的子菜单项，而是会显示自定义组件，并且将所有 props 传递给自定义组件。\n    items?: DragButtonItemProps[];                                // 子菜单项列表，如果提供了该属性，则视为扩展目标菜单项的二级菜单。\n  };\n}\n```\n\n## 7. 块缩进扩展\n\n编辑器会根据节点的 schema 元数据发现可缩进节点，不维护组件名称白名单。第三方节点只要属于 `block` group，且不属于 `list` group，就会自动获得块缩进属性、快捷键和拖拽缩进能力：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MyBlock = Node.create({\n  name: \"myBlock\",\n  group: \"block\",\n  // ...\n});\n```\n\n特殊节点可以通过 `haloEditorIndentation` 调整接入行为：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MyBlock = Node.create({\n  name: \"myBlock\",\n  group: \"block\",\n\n  // 光标在节点内部时，将 Tab / Shift-Tab 交给节点自身处理；\n  // 节点选中或光标位于节点左上角间隙时，仍可缩进整个节点。\n  haloEditorIndentation: {\n    keyboard: \"passthrough\",\n  },\n});\n```\n\n- 设置为 `false` 可以让 block 节点退出通用缩进。\n- 设置为 `true` 可以让不属于 block group 的特殊节点显式接入缩进。\n- `legacyLineIndent: true` 仅用于需要兼容旧版首行缩进数据的文本节点，不建议新扩展启用。\n\n自定义列表容器应加入 `list` group，列表项应加入 `listItem` group。编辑器通过这两个 schema group 识别列表层级、继承缩进和行内拖拽目标，不依赖 `bulletList`、`orderedList`、`listItem` 等具体节点名称。\n\n第三方块命令可以复用以下公共 helpers：\n\n```ts\nimport {\n  findAncestorListItems,\n  getBlockIndentAtSelection,\n  prepareBlockCommandFromList,\n} from \"@halo-dev/richtext-editor\";\n\nconst listItems = findAncestorListItems(editor.state.selection.$from);\nconst indent = getBlockIndentAtSelection(editor);\nconst preparedRange = prepareBlockCommandFromList(editor, range);\n```\n\n- `findAncestorListItems` 按 schema group 查找当前光标所在的列表项，结果从内层到外层排列。\n- `getBlockIndentAtSelection` 将显式块缩进和列表层级换算为当前配置下的可视缩进。\n- `prepareBlockCommandFromList` 适用于 Slash Command 一类块命令：移除触发文本、退出列表并保留可视缩进，且整个操作可以一次撤销。\n\n缩进步长、最小值、最大值和默认值都可以通过 `ExtensionsKit` 配置：\n\n```ts\nimport { ExtensionsKit } from \"@halo-dev/richtext-editor\";\n\nExtensionsKit.configure({\n  indent: {\n    indentRange: 32,\n    minIndentLevel: 0,\n    maxIndentLevel: 320,\n    defaultIndentLevel: 0,\n  },\n});\n```\n\n未配置 `maxIndentLevel` 时，默认允许 10 级缩进，并会随 `indentRange` 自动换算最大值。\n","isInternal":false,"tokens":7291,"sizeBytes":26266},{"name":"runtime-metadata.md","path":"ui/packages/editor/docs/runtime-metadata.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/ui/packages/editor/docs/runtime-metadata.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# 编辑器扩展的运行期元数据\n\n`@halo-dev/richtext-editor` 允许 Tiptap 的 Node、Mark 和 Extension\n声明运行期组件元数据，用于描述最终 Editor 实例中的 schema、组件用法、结构关系、\n属性和示例。这些信息从 Editor 当前注册的扩展及最终 schema 中同步解析；AI Agent\n是目前的主要消费者，但元数据本身是对运行期组件能力的描述，不会改变或约束组件行为。\n\n## 声明新组件\n\n下面的数学公式节点说明了适用场景、属性和生成所需的外部能力：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MathBlock = Node.create({\n  name: \"mathBlock\",\n  group: \"block\",\n  atom: true,\n\n  addAttributes() {\n    return {\n      formula: {\n        default: \"\",\n      },\n    };\n  },\n\n  parseHTML() {\n    return [{ tag: 'div[data-type=\"math-block\"]' }];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    return [\"div\", { ...HTMLAttributes, \"data-type\": \"math-block\" }];\n  },\n\n  // 声明运行期元数据中的 AI 使用说明\n  addHaloEditorMetadata() {\n    return {\n      ai: {\n        description: \"A display mathematical formula.\",\n        exposure: \"available\",\n        useWhen: [\"Presenting a standalone mathematical expression.\"],\n        attributeGuidance: {\n          formula: {\n            description: \"Formula source written in LaTeX.\",\n            format: \"LaTeX\",\n            examples: [\"E = mc^2\"],\n          },\n        },\n        generation: {\n          mode: \"requires-capability\",\n          requiredCapabilities: [\"math-to-html\"],\n        },\n        examples: [\n          '<div data-type=\"math-block\" formula=\"E = mc^2\"></div>',\n        ],\n      },\n    };\n  },\n});\n```\n\nCapability 名称是开放字符串。Halo 只把它放入 Manifest，不负责查找或执行对应工具，由最终使用者扩展工具。\n\n## 扩展现有组件\n\n元数据与 Tiptap 的 `.extend()` 继承链一起自动合并。插件只需返回自己的局部补丁，\n不需要调用 `this.parent`，也不需要手动合并 Halo 已有说明：\n\n```ts\nimport { ExtensionCodeBlock } from \"@halo-dev/richtext-editor\";\n\nexport const HighlightedCodeBlock = ExtensionCodeBlock.extend({\n  addAttributes() {\n    return {\n      ...this.parent?.(),\n      highlightTheme: {\n        default: null,\n      },\n    };\n  },\n\n  addHaloEditorMetadata() {\n    return {\n      ai: {\n        attributeGuidance: {\n          highlightTheme: {\n            description: \"Syntax-highlighting theme.\",\n            allowedValues: [\"github-light\", \"github-dark\"],\n            omitWhen: [\"The editor default theme should be used.\"],\n          },\n        },\n      },\n    };\n  },\n});\n```\n\n最终 Manifest 会同时包含原 code block 的描述和 `highlightTheme`。\n\n## 为全局属性贡献说明\n\nPlain Extension 不会成为 Manifest 组件，但可向明确命名的 Node 或 Mark\n贡献元数据。这适合 `addGlobalAttributes()`：\n\n```ts\nimport { Extension } from \"@halo-dev/richtext-editor\";\n\nexport const Tone = Extension.create({\n  name: \"tone\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"paragraph\", \"heading\"],\n        attributes: {\n          tone: {\n            default: null,\n          },\n        },\n      },\n    ];\n  },\n\n  addHaloEditorMetadata() {\n    return {\n      contributions: [\n        {\n          targets: [\n            { kind: \"node\", name: \"paragraph\" },\n            { kind: \"node\", name: \"heading\" },\n          ],\n          metadata: {\n            ai: {\n              attributeGuidance: {\n                tone: {\n                  description: \"Writing tone for this block.\",\n                  allowedValues: [\"neutral\", \"friendly\", \"formal\"],\n                },\n              },\n            },\n          },\n        },\n      ],\n    };\n  },\n});\n```\n\n贡献只应用于最终 schema 中存在的目标。多个贡献冲突时，根据组件定义中的 priority 决定，\n较高者优先；priority 相同时，后注册者优先。\n\n## 组件自身的结构说明\n\n组件只声明自己的父级和数量关系，不跨组件声明子节点：\n\n```ts\naddHaloEditorMetadata() {\n  return {\n    ai: {\n      description: \"An optional caption belonging to a figure.\",\n    },\n    structure: {\n      allowedParents: [\"figure\"],\n      minPerParent: 0,\n      maxPerParent: 1,\n    },\n  };\n}\n```\n\n这表示当前组件只能位于 `figure` 下，在每个 `figure` 中可省略且最多出现一次。\n\n## 禁用与兼容\n\n- `ai: false` 明确建议 AI 不主动使用该组件，但组件的 schema 信息仍会出现在\n  Manifest 中供 AI 理解。\n- 声明抛错、字段无效、属性或父节点不存在时，生成器保留其他有效数据；\n  开发环境给出警告，生产环境不会因元数据阻止编辑器运行。\n- 未知字段会被丢弃。请勿把 system prompt、可执行回调或敏感信息放入元数据。\n\n每段文本最多 1,000 个字符；说明数组和 aliases 最多 10 项；\n`allowedValues` 和属性示例最多 32 项；组件 HTML 示例最多 3 个且每个不超过\n4 KiB；单组件 AI 元数据最多 16 KiB；单个 Manifest 的 AI 元数据最多\n128 KiB。\n\n## 读取运行期 Manifest\n\n在 Editor 创建完成后同步生成最终快照：\n\n```ts\nimport {\n  createHaloEditorManifest,\n  type HaloEditorManifest,\n  type VueEditor,\n} from \"@halo-dev/richtext-editor\";\n\nfunction editorManifest(editor: VueEditor): HaloEditorManifest {\n  return createHaloEditorManifest(editor);\n}\n```\n\nManifest 是当前 Editor 实例的运行期快照，包含全部 Node 和 Mark、规范化元数据、\n`version: 1` 以及稳定的 `signature`。AI 插件可以把它加入模型上下文，其他消费者\n也可以用它了解当前编辑器实际注册的组件；消费者自行决定是否根据其中的建议进行额外校验。\n","isInternal":false,"tokens":1587,"sizeBytes":5802},{"name":"apply.md","path":".claude/commands/opsx/apply.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/commands/opsx/apply.md","title":"OPSX: Apply","category":"command-prompt","format":"markdown","content":"---\nname: \"OPSX: Apply\"\ndescription: Implement tasks from an OpenSpec change (Experimental)\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using `/opsx:continue`\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! You can archive this change with `/opsx:archive`.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n","frontmatter":{"name":"OPSX: Apply","description":"Implement tasks from an OpenSpec change (Experimental)","allowed-tools":"Bash(openspec:*)","category":"Workflow","tags":["workflow","artifacts","experimental"],"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1238,"sizeBytes":5287},{"name":"archive.md","path":".claude/commands/opsx/archive.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/commands/opsx/archive.md","title":"OPSX: Archive","category":"command-prompt","format":"markdown","content":"---\nname: \"OPSX: Archive\"\ndescription: Archive a completed change in the experimental workflow\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, archive, experimental]\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Prompt user for confirmation to continue\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Prompt user for confirmation to continue\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Spec sync status (synced / sync skipped / no delta specs)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs\n\nAll artifacts complete. All tasks complete.\n```\n\n**Output On Success (No Delta Specs)**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** No delta specs\n\nAll artifacts complete. All tasks complete.\n```\n\n**Output On Success With Warnings**\n\n```\n## Archive Complete (with warnings)\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** Sync skipped (user chose to skip)\n\n**Warnings:**\n- Archived with 2 incomplete artifacts\n- Archived with 3 incomplete tasks\n- Delta spec sync was skipped (user chose to skip)\n\nReview the archive if this was not intentional.\n```\n\n**Output On Error (Archive Exists)**\n\n```\n## Archive Failed\n\n**Change:** <change-name>\n**Target:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n\nTarget archive directory already exists.\n\n**Options:**\n1. Rename the existing archive\n2. Delete the existing archive if it's a duplicate\n3. Wait until a different date to archive\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use the Skill tool to invoke `openspec-sync-specs` (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n","frontmatter":{"name":"OPSX: Archive","description":"Archive a completed change in the experimental workflow","allowed-tools":"Bash(openspec:*)","category":"Workflow","tags":["workflow","archive","experimental"],"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1387,"sizeBytes":5916},{"name":"explore.md","path":".claude/commands/opsx/explore.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/commands/opsx/explore.md","title":"OPSX: Explore","category":"command-prompt","format":"markdown","content":"---\nname: \"OPSX: Explore\"\ndescription: \"Enter explore mode - think through ideas, investigate problems, clarify requirements\"\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, explore, experimental, thinking]\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The argument after `/opsx:explore` is whatever the user wants to think about. Could be:\n- A vague idea: \"real-time collaboration\"\n- A specific problem: \"the auth system is getting unwieldy\"\n- A change name: \"add-dark-mode\" (to explore in context of that change)\n- A comparison: \"postgres vs sqlite for this\"\n- Nothing (just enter explore mode)\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\nIf the user mentioned a specific change name, read its artifacts for context.\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen things crystallize, you might offer a summary - but it's optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n","frontmatter":{"name":"OPSX: Explore","description":"Enter explore mode - think through ideas, investigate problems, clarify requirements","allowed-tools":"Bash(openspec:*)","category":"Workflow","tags":["workflow","explore","experimental","thinking"],"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1575,"sizeBytes":7450},{"name":"propose.md","path":".claude/commands/opsx/propose.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/commands/opsx/propose.md","title":"OPSX: Propose","category":"command-prompt","format":"markdown","content":"---\nname: \"OPSX: Propose\"\ndescription: Propose a new change - create it and generate all artifacts in one step\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The argument after `/opsx:propose` is the change name (kebab-case), OR a description of what the user wants to build.\n\n**Steps**\n\n1. **If no input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` to start implementing.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n","frontmatter":{"name":"OPSX: Propose","description":"Propose a new change - create it and generate all artifacts in one step","allowed-tools":"Bash(openspec:*)","category":"Workflow","tags":["workflow","artifacts","experimental"],"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1178,"sizeBytes":5237},{"name":"sync.md","path":".claude/commands/opsx/sync.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/commands/opsx/sync.md","title":"OPSX: Sync","category":"command-prompt","format":"markdown","content":"---\nname: \"OPSX: Sync\"\ndescription: Sync delta specs from a change to main specs\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, specs, experimental]\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n","frontmatter":{"name":"OPSX: Sync","description":"Sync delta specs from a change to main specs","allowed-tools":"Bash(openspec:*)","category":"Workflow","tags":["workflow","specs","experimental"],"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1154,"sizeBytes":5058},{"name":"update.md","path":".claude/commands/opsx/update.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/.claude/commands/opsx/update.md","title":"OPSX: Update","category":"command-prompt","format":"markdown","content":"---\nname: \"OPSX: Update\"\ndescription: Update a change - revise existing planning artifacts and keep them coherent (Experimental)\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name after `/opsx:update` (e.g., `/opsx:update add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n","frontmatter":{"name":"OPSX: Update","description":"Update a change - revise existing planning artifacts and keep them coherent (Experimental)","allowed-tools":"Bash(openspec:*)","category":"Workflow","tags":["workflow","artifacts","experimental"],"allowedTools":["Bash(openspec:*)"]},"allowedTools":["Bash(openspec:*)"],"isInternal":false,"tokens":1261,"sizeBytes":5615},{"name":"README.md","path":"ui/packages/api-client/README.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/ui/packages/api-client/README.md","title":"api-client Documentation","category":"plugin-manifest","format":"markdown","content":"# @halo-dev/api-client\n\nHalo 2.0 的 JavaScript API 客户端请求库。使用 [OpenAPI Generator](https://openapi-generator.tech/) 生成。\n\n## 使用\n\n```javascript\nimport {\n  coreApiClient,\n  consoleApiClient,\n  ucApiClient,\n  publicApiClient,\n  createCoreApiClient,\n  createConsoleApiClient,\n  createUcApiClient,\n  createPublicApiClient,\n  axiosInstance,\n} from \"@halo-dev/api-client\";\n```\n\n- **coreApiClient**: 为 Halo 所有自定义模型的 CRUD 接口封装的 api client。\n- **consoleApiClient**: 为 Halo 针对 Console 提供的接口封装的 api client。\n- **ucApiClient**: 为 Halo 针对 UC 提供的接口封装的 api client。\n- **publicApiClient**: 为 Halo 所有公开访问的接口封装的 api client。\n- **createCoreApiClient**: 用于创建自定义模型的 CRUD 接口封装的 api client，需要传入 axios 实例。\n- **createConsoleApiClient**: 用于创建 Console 接口封装的 api client，需要传入 axios 实例。\n- **createUcApiClient**: 用于创建 UC 接口封装的 api client，需要传入 axios 实例。\n- **createPublicApiClient**: 用于创建公开访问接口封装的 api client，需要传入 axios 实例。\n- **axiosInstance**: 内部默认创建的 axios 实例。\n\n### 在插件中使用\n\n```shell\npnpm install @halo-dev/api-client axios\n```\n\n由于已经在 Console 和 UC 项目中引入并设置好了 Axios 拦截器，所以直接使用即可：\n\n```javascript\nimport { coreApiClient } from \"@halo-dev/api-client\";\n\ncoreApiClient.content.post.listPost().then((response) => {\n  // handle response\n});\n```\n\n此外，在最新的 `@halo-dev/ui-plugin-bundler-kit@2.17.0` 中，已经排除了 `@halo-dev/api-client`、`axios` 依赖，所以最终产物中的相关依赖会自动使用 Halo 本身提供的依赖，无需关心最终产物大小。\n\n详细文档可查阅：[插件开发 / API 请求](https://docs.halo.run/developer-guide/plugin/api-reference/ui/api-request)\n\n### 在外部项目中使用\n\n```shell\npnpm install @halo-dev/api-client axios\n```\n\n```javascript\nimport axios from \"axios\";\n\nconst axiosInstance = axios.create({\n  baseURL: \"http://localhost:8090\",\n});\n\nconst coreApiClient = createCoreApiClient(axiosInstance);\n\ncoreApiClient.content.post.listPost().then((response) => {\n  // handle response\n});\n```\n","isInternal":false,"tokens":643,"sizeBytes":2301},{"name":"README.md","path":"ui/packages/console-shared/README.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/ui/packages/console-shared/README.md","title":"console-shared Documentation","category":"plugin-manifest","format":"markdown","content":"Please use `@halo-dev/ui-shared`, this package is only for compatibility.\n","isInternal":false,"tokens":17,"sizeBytes":74},{"name":"README.md","path":"ui/packages/editor/README.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/ui/packages/editor/README.md","title":"editor Documentation","category":"plugin-manifest","format":"markdown","content":"# @halo-dev/richtext-editor\n\nThe default editor for Halo, built with [Tiptap](https://tiptap.dev/) + [ProseMirror](https://prosemirror.net/).\n\n> ⚠️ This package is not suitable for general editor use cases, as it is tightly coupled with Halo and uses some of Halo's built-in tools and shared dependencies.\n> However, you can use it in Halo plugins just like you would use it in Halo itself.\n\n## Installation\n\nIn Halo plugins:\n\n```bash\npnpm install @halo-dev/richtext-editor\n```\n\n## Usage\n\n```vue\n<script setup lang=\"ts\">\nimport {\n  ExtensionsKit,\n  RichTextEditor,\n  VueEditor,\n} from \"@halo-dev/richtext-editor\";\nimport { onMounted, ref, shallowRef } from \"vue\";\n\nconst editor = shallowRef<VueEditor>();\nconst content = ref(\"Hello World\");\n\nonMounted(() => {\n  editor.value = new VueEditor({\n    content: content.value,\n    extensions: [ExtensionsKit],\n    parseOptions: {\n      preserveWhitespace: true,\n    },\n    onUpdate: () => {\n      content.value = editor.value?.getHTML() || \"\";\n    },\n  });\n});\n</script>\n<template>\n  <RichTextEditor v-if=\"editor\" :editor=\"editor\" />\n</template>\n```\n\n## Requirements\n\n- Vue 3.5.x or higher\n- Halo plugin environment\n\n## Extension development\n\n- [Editor extensions, toolbar items, and keyboard shortcuts](./docs/extension.md)\n- [Editor extension runtime metadata](./docs/runtime-metadata.md)\n\n## Links\n\n- [Halo](https://github.com/halo-dev/halo)\n- [Documentation](https://docs.halo.run)\n- [Report Issues](https://github.com/halo-dev/halo/issues)\n\n## License\n\nGPL-3.0\n","isInternal":false,"tokens":389,"sizeBytes":1514},{"name":"README.md","path":"ui/packages/ui-plugin-bundler-kit/README.md","rawUrl":"https://raw.githubusercontent.com/halo-dev/halo/HEAD/ui/packages/ui-plugin-bundler-kit/README.md","title":"ui-plugin-bundler-kit Documentation","category":"plugin-manifest","format":"markdown","content":"# @halo-dev/ui-plugin-bundler-kit\n\nA frontend build toolkit for Halo UI plugin development, supporting both Vite and Rsbuild build systems.\n\n## Introduction\n\n`@halo-dev/ui-plugin-bundler-kit` is a frontend build configuration toolkit specifically designed for Halo UI plugin development. It provides pre-configured build settings to help developers quickly set up and build frontend interfaces for Halo plugins and theme-provided UI plugins.\n\n### Key Features\n\n- 🚀 **Ready to Use** - Provides pre-configured Vite and Rsbuild build settings\n- 📦 **Multi-Build Tool Support** - Supports both Vite and Rsbuild\n- 🔧 **Flexible Configuration** - Supports custom build configurations\n- 🎯 **Halo Optimized** - External dependencies and global variables optimized for Halo UI plugin development\n- 📁 **Smart Output** - Automatically selects output directory based on environment\n\n## Installation\n\n```bash\n# Using npm\nnpm install @halo-dev/ui-plugin-bundler-kit\n\n# Using yarn\nyarn add @halo-dev/ui-plugin-bundler-kit\n\n# Using pnpm\npnpm add @halo-dev/ui-plugin-bundler-kit\n```\n\n### Additional Dependencies\n\n**For Vite users**, install Vite and its Vue plugin:\n\n```bash\nnpm install vite @vitejs/plugin-vue\n```\n\n**For Rsbuild users**, install Rsbuild and its Vue plugin:\n\n```bash\nnpm install @rsbuild/core @rsbuild/plugin-vue\n```\n\nThe Vite and Rsbuild peer dependencies are optional. Install only the build tool and Vue plugin you use, and import its configuration helper from `@halo-dev/ui-plugin-bundler-kit/vite` or `@halo-dev/ui-plugin-bundler-kit/rsbuild`. The package root exports and the deprecated `HaloUIPluginBundlerKit` helper have been removed.\n\n## Usage\n\n### Vite Configuration\n\nCreate or update `vite.config.ts` file in your UI plugin project root:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  // provider defaults to \"plugin\"\n  vite: {\n    // Your custom Vite configuration\n    plugins: [\n      // Additional plugins (Vue plugin is already included)\n    ],\n    // Other configurations...\n  },\n});\n```\n\n> **Note**: Vue plugin is pre-configured. Pass its options through the top-level `vue` field instead of adding another `@vitejs/plugin-vue` instance.\n\n### Rsbuild Configuration\n\nCreate or update `rsbuild.config.ts` file in your UI plugin project root:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  // provider defaults to \"plugin\"\n  rsbuild: {\n    // Your custom Rsbuild configuration\n    plugins: [\n      // Additional plugins (Vue plugin is already included)\n    ],\n    // Other configurations...\n  },\n});\n```\n\n> **Note**: Vue plugin is pre-configured. Pass its options through the top-level `vue` field instead of adding another `@rsbuild/plugin-vue` instance.\n\n### Theme UI Plugin Configuration\n\nFor theme-provided Console/User Center UI plugins, place the frontend project under the theme's `ui-plugin/` directory:\n\n```text\ntheme-root/\n├── theme.yaml\n└── ui-plugin/\n    ├── package.json\n    ├── src/index.ts\n    └── vite.config.ts\n```\n\nVite:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  provider: \"theme\",\n  vite: {},\n});\n```\n\nRsbuild:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  provider: \"theme\",\n  rsbuild: {},\n});\n```\n\nThe theme provider reads `../theme.yaml`, outputs to `dist`, registers the module as `theme:{metadata.name}`, and configures assets for `/themes/{metadata.name}/ui-plugin/assets/`. Halo reads only `ui-plugin/dist/**` from the theme package.\n\n### Output Format and Halo Target\n\n`viteConfig` and `rsbuildConfig` accept the same format options:\n\n```typescript\nexport default viteConfig({\n  format: \"auto\", // \"auto\" | \"iife\" | \"esm\"\n  vite: {},\n});\n```\n\n`auto` is the default. It emits ESM when `spec.requires` is a stable Halo version or a simple `>=MAJOR.MINOR.PATCH` target whose minimum is Halo 2.26.0 or newer. Missing, wildcard, composite, or otherwise unsupported ranges produce a warning and keep the compatible IIFE output. This fallback is intentional: ESM is optional, and Halo continues loading old IIFE plugin and theme artifacts throughout Halo 2.x.\n\nUse an explicit target only when intentionally forcing ESM and a target cannot be derived:\n\n```typescript\nexport default viteConfig({\n  format: \"esm\",\n  targetHaloVersion: \"2.26.0\",\n  vite: {},\n});\n```\n\nAfter ESM is selected, successful builds generate `ui-plugin.json`. This filename is reserved for the bundler kit: do not create, copy, or emit another file with that name. A provider artifact without this file remains legacy, even if its `spec.requires` also supports Halo 2.26 or newer.\n\nESM entries must default-export the existing `PluginModule`. The generated manifest records one optional main stylesheet; CSS belonging to asynchronous chunks stays out of the manifest and loads on demand with its JavaScript chunk. Halo starts every provider-owned startup stylesheet and entry in parallel, then commits modules in provider order without reloading after each entry. Top-level module effects, timers, listeners, and arbitrary asynchronous effects are not transactional; a full page reload remains the lifecycle and recovery boundary after provider changes.\n\nThe default Vite and Rsbuild presets provide ESM output, provider-relative resource paths, content-hashed entry and startup-style filenames, content-hashed secondary resources, and Halo shared-runtime externals. The generated manifest records the actual startup filenames. Raw `vite` and `rsbuild` configuration is merged after these defaults and is not inspected, rejected, or rewritten. If it changes entry names, formats, public paths, externals, or caching behavior, the caller owns the resulting manifest consistency, Import Map compatibility, shared dependency identity, resource relocation, and cache safety. Compatible IIFE overrides retain their previous stable `main.js` behavior.\n\n### Shared Runtime Dependencies\n\nESM providers may import these package roots from Halo:\n\n- `vue`\n- `vue-router`\n- `pinia`\n- `axios`\n- `@formkit/vue`\n- `@formkit/core`\n- `@halo-dev/ui-shared`\n- `@halo-dev/components`\n- `@halo-dev/api-client`\n- `@halo-dev/richtext-editor`\n\nThe bundler discovers shared package root imports and reports the installed provider version beside the selected Halo host snapshot version when package metadata is available. A newer provider dependency emits a compatibility note, and a different major emits a stronger note, but version drift does not fail the build. Export usage, aliases, forks, and final bundler resolution are not inspected. Deep imports such as `vue/dist/vue.esm-bundler.js` still fail because Halo's Import Map exposes shared package roots only.\n\nVue, Vue Router, Pinia, and the FormKit Vue/Core graph share host identity. Other `@formkit/*` packages and VueUse stay provider-private. Non-shared dependencies are bundled by the default presets.\n\nThe shared `axios` import is the standard package module. Do not mutate its shared defaults or interceptors. For isolated clients, call `axios.create()`. `@halo-dev/api-client` exports Halo's separate authenticated `axiosInstance`; do not mutate that instance either.\n\n### Querying Other UI Providers\n\nUse the shared registration store instead of checking another provider's `window.PluginName` global:\n\n```typescript\nimport { stores } from \"@halo-dev/ui-shared\";\n\nconst uiPlugins = stores.uiPlugins();\n\nuiPlugins.isEnabled(\"plugin-search\");\nuiPlugins.isRegistered(\"plugin-search\");\nuiPlugins.get(\"plugin-search\");\n```\n\nThe reactive record contains only Halo-owned `name`, `type`, `version`, and `pending | registered | failed` status. `isEnabled` means the provider was discovered in the current descriptor; `isRegistered` becomes true after its current-page registration succeeds. Provider code treats this store as read-only and must not depend on another provider's evaluation order or module object.\n\n## Configuration Options\n\n### Vite Configuration Options\n\n```typescript\ninterface ViteUserConfig {\n  /**\n   * UI plugin provider type\n   * @default \"plugin\"\n   */\n  provider?: \"plugin\" | \"theme\";\n\n  /**\n   * Halo plugin or theme manifest file path\n   * @default \"../src/main/resources/plugin.yaml\" for plugins, \"../theme.yaml\" for themes\n   */\n  manifestPath?: string;\n\n  /** @default \"auto\" */\n  format?: \"auto\" | \"iife\" | \"esm\";\n\n  /** Required for explicit ESM when spec.requires has no derivable target. */\n  targetHaloVersion?: string;\n\n  /** Options for the built-in @vitejs/plugin-vue instance. */\n  vue?: VuePluginOptions;\n\n  /**\n   * Custom Vite configuration\n   */\n  vite: UserConfig | UserConfigFnObject;\n}\n```\n\n### Rsbuild Configuration Options\n\n```typescript\ninterface RsBuildUserConfig {\n  /**\n   * UI plugin provider type\n   * @default \"plugin\"\n   */\n  provider?: \"plugin\" | \"theme\";\n\n  /**\n   * Halo plugin or theme manifest file path\n   * @default \"../src/main/resources/plugin.yaml\" for plugins, \"../theme.yaml\" for themes\n   */\n  manifestPath?: string;\n\n  /** @default \"auto\" */\n  format?: \"auto\" | \"iife\" | \"esm\";\n\n  /** Required for explicit ESM when spec.requires has no derivable target. */\n  targetHaloVersion?: string;\n\n  /** Options for the built-in @rsbuild/plugin-vue instance. */\n  vue?: PluginVueOptions;\n\n  /**\n   * Custom Rsbuild configuration\n   */\n  rsbuild: RsbuildConfig | ((env: ConfigParams) => RsbuildConfig);\n}\n```\n\n## Advanced Configuration Examples\n\n### Customizing the Vue Compiler\n\nVite:\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  vue: {\n    template: {\n      compilerOptions: {\n        isCustomElement: (tag) => tag === \"halo-app-card\",\n      },\n    },\n  },\n  vite: {},\n});\n```\n\nRsbuild:\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  vue: {\n    vueLoaderOptions: {\n      compilerOptions: {\n        isCustomElement: (tag) => tag === \"halo-app-card\",\n      },\n    },\n  },\n  rsbuild: {},\n});\n```\n\nThe helper owns the Vue plugin instance. Keep `@vitejs/plugin-vue` and `@rsbuild/plugin-vue` out of the nested `plugins` array to avoid running the SFC transform twice.\n\n### Adding Path Aliases (Vite)\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\nimport path from \"path\";\n\nexport default viteConfig({\n  vite: {\n    resolve: {\n      alias: {\n        \"@\": path.resolve(__dirname, \"src\"),\n        \"@components\": path.resolve(__dirname, \"src/components\"),\n      },\n    },\n  },\n});\n```\n\n### Adding Path Aliases (Rsbuild)\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\n\nexport default rsbuildConfig({\n  rsbuild: {\n    source: {\n      alias: {\n        \"@\": \"./src\",\n        \"@components\": \"./src/components\",\n      },\n    },\n  },\n});\n```\n\n### Adding Additional Vite Plugins\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\nimport { defineConfig } from \"vite\";\nimport UnoCSS from \"unocss/vite\";\n\nexport default viteConfig({\n  vite: {\n    plugins: [\n      UnoCSS(), // Add UnoCSS plugin\n    ],\n  },\n});\n```\n\n### Adding Additional Rsbuild Plugins\n\n```typescript\nimport { rsbuildConfig } from \"@halo-dev/ui-plugin-bundler-kit/rsbuild\";\nimport { pluginSass } from \"@rsbuild/plugin-sass\";\n\nexport default rsbuildConfig({\n  rsbuild: {\n    plugins: [\n      pluginSass(), // Add Sass plugin\n    ],\n  },\n});\n```\n\n### Custom Plugin Manifest Path\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  manifestPath: \"application/src/main/resources/plugin.yaml\", // Custom manifest file path\n  vite: {\n    // Other configurations...\n  },\n});\n```\n\n### Custom Theme Manifest Path\n\n```typescript\nimport { viteConfig } from \"@halo-dev/ui-plugin-bundler-kit/vite\";\n\nexport default viteConfig({\n  provider: \"theme\",\n  manifestPath: \"../custom-theme.yaml\",\n  vite: {\n    // Other configurations...\n  },\n});\n```\n\n## Development Scripts\n\nRecommended scripts to add to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"vite dev --mode=development --watch\",\n    \"build\": \"vite build\"\n  }\n}\n```\n\nFor Rsbuild:\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"rsbuild dev --env-mode=development --watch\",\n    \"build\": \"rsbuild build\"\n  }\n}\n```\n\n## Build Output\n\n> Relative to the UI plugin project root\n\nPlugin provider:\n\n- **Development**: `../build/resources/main/ui` or `../build/resources/main/console`\n- **Production**: `./build/dist`\n\nTheme provider:\n\n- **Development**: `dist`\n- **Production**: `dist`\n\nAn ESM output additionally contains the reserved, generated `ui-plugin.json` manifest and may contain content-hashed `chunks/` and `assets/`. The manifest contains `format`, the actual content-hashed `entry`, and an optional content-hashed `style`; asynchronous chunk CSS is not listed. Keep the complete output directory together. Halo serves these canonical paths through the existing plugin or theme static resource mapping without adding query cache keys, so imports back to the entry resolve to the same ESM module URL. Callers that replace the default content-hashed filenames accept the risk of stale resources under Halo's production static-resource cache. Legacy IIFE resources retain their version query behavior. The Halo 2.x compatibility `bundle.css` endpoint remains available for older callers, but now contains ordered `@import` rules pointing at those direct styles so relative asset URLs keep the correct provider base; it is not used by the new runtime.\n\n## Maintaining Halo Host Runtime Snapshots\n\nSnapshots capture the exact host version, statically importable root exports that are also present on the browser runtime global, bridge global, and identity category for each shared root. Synthetic namespace metadata such as `__esModule` is excluded when the browser global does not expose it. Snapshots do not claim an accepted provider version range. They are intentionally sparse: add a new immutable snapshot when Halo's shared runtime contract changes, not automatically for every Halo patch. A newer target selects the latest eligible older snapshot and emits a forward-compatibility warning; a target older than every packaged snapshot cannot build ESM.\n\nWhen the host dependency graph changes:\n\n1. Set `@halo-dev/ui-plugin-bundler-kit` to the Halo version represented by the snapshot.\n2. Run `pnpm --filter @halo-dev/ui-plugin-bundler-kit snapshot:generate` to derive the versioned output path and capture resolved host versions and exports.\n3. Run `pnpm --filter @halo-dev/ui-plugin-bundler-kit snapshot:check` and the compatibility fixtures before publishing.\n4. Preserve older snapshot files while supported provider artifacts may still target them.\n\nSnapshot generation is currently an explicit maintainer action; it is not coupled to every package build or CI job.\n\n## Requirements\n\n- **Node.js**: ^18.0.0 || >=20.0.0\n- **Peer Dependencies**:\n  - `@rsbuild/core`: ^1.0.0 || ^2.0.0 (when using Rsbuild)\n  - `@rsbuild/plugin-vue`: ^1.0.0 || ^2.0.0 (when using Rsbuild)\n  - `@vitejs/plugin-vue`: ^5.0.0 || ^6.0.0 (when using Vite)\n  - `vite`: ^6.0.0 || ^7.0.0 || ^8.0.0 (when using Vite)\n\n## Choosing Between Vite and Rsbuild\n\n**Vite is the recommended default for new UI plugins.** Halo 2.26.0 and later can load ESM plugin resources, so Vite builds can use dynamic imports and emit separate asynchronous chunks. Rsbuild was originally introduced to support code splitting while UI plugins were limited to legacy IIFE output; that is no longer a reason to prefer Rsbuild when targeting Halo 2.26.0 or later.\n\n### Use Vite by Default\n\n- **ESM code splitting** - Supports lazy loading and asynchronous chunks when the target Halo version selects ESM output.\n- **Broader ecosystem** - Provides wider plugin coverage and closer alignment with Vue ecosystem tooling.\n- **Strong build performance** - Recent Vite versions have substantially improved build performance, so build speed alone is usually not a reason to choose Rsbuild.\n- **Familiar configuration** - Keeps plugin projects aligned with the tooling commonly used by Vue applications.\n\n### Use Rsbuild When Needed\n\nChoose Rsbuild when at least one of these conditions applies:\n\n- The plugin must keep legacy IIFE output, such as when targeting Halo versions earlier than 2.26.0, but still needs code splitting.\n- The project already uses Rsbuild or depends on Rsbuild/Rspack-specific plugins and configuration.\n- Measurements on the actual project show a meaningful build-performance advantage.\n\nDo not choose a build system based only on plugin size. With ESM output, both Vite and Rsbuild support dynamic imports and code splitting. The following pattern works with either preset:\n\n```typescript\nimport { definePlugin } from \"@halo-dev/ui-shared\";\nimport { defineAsyncComponent } from \"vue\";\nimport { VLoading } from \"@halo-dev/components\";\n\nexport default definePlugin({\n  routes: [\n    {\n      parentName: \"Root\",\n      route: {\n        path: \"demo\",\n        name: \"DemoPage\",\n        // Lazy load heavy components\n        component: defineAsyncComponent({\n          loader: () => import(\"./views/DemoPage.vue\"),\n          loadingComponent: VLoading,\n        }),\n      },\n    },\n  ],\n  extensionPoints: {},\n});\n```\n\n### Summary\n\n| Consideration              | Vite                                       | Rsbuild                                         |\n| -------------------------- | ------------------------------------------ | ----------------------------------------------- |\n| Recommended use            | New plugins targeting Halo 2.26.0 or later | Existing Rsbuild projects or legacy IIFE builds |\n| ESM code splitting         | Supported                                  | Supported                                       |\n| Legacy IIFE code splitting | Not supported by the default preset        | Supported                                       |\n| Plugin ecosystem           | Broader Vite and Vue ecosystem             | Smaller, focused on Rsbuild and Rspack          |\n| Build performance          | Fast; recommended default                  | Fast; benchmark when performance is decisive    |\n\n## License\n\nGPL-3.0\n\n## Contributing\n\nIssues and Pull Requests are welcome! Please check our [Contributing Guide](https://github.com/halo-dev/halo/blob/main/CONTRIBUTING.md) for more information.\n\n## Related Links\n\n- [Halo Website](https://www.halo.run/)\n- [Halo Documentation](https://docs.halo.run/)\n- [GitHub Repository](https://github.com/halo-dev/halo)\n- [Plugin Development Guide](https://docs.halo.run/developer-guide/plugin/basics/ui/)\n","isInternal":false,"tokens":4245,"sizeBytes":18757}],"systemPromptSnippet":"<yakaai_skills repo=\"halo-dev/halo\">\n<!-- File: AGENTS.md (Tokens: ~473 | Category: root-instruction) -->\n# Repository Guidelines\n\nHalo is a full-stack monorepo: Java 21 / Spring Boot WebFlux + R2DBC backend and a Vue 3 + TypeScript + TailwindCSS frontend. Work from the repo root and load **every relevant module guide** for the areas you touch.\n\n## Modules\n\n|       Module        |                           Path                            |\n|---------------------|-----------------------------------------------------------|\n| API library         | `api/` (extension model, contracts, security)             |\n| Backend application | `application/` (services, routers, migrations, packaging) |\n| Frontend            | `ui/` (console, user center, workspace packages)          |\n| Application BOM     | `platform/application/` (shared dependency constraints)   |\n| Plugin BOM          | `platform/plugin/` (plugin-facing constraints)            |\n\n## Quick Commands\n\nRun from the repo root:\n\n- `./gradlew build` — full backend build and tests.\n- `./gradlew spotlessApply` — format Java, Markdown, JSON, and properties.\n- `./gradlew :application:test` — backend tests.\n- `pnpm -C ui typecheck && pnpm -C ui lint` — frontend validation.\n- `pnpm -C ui test:unit` — frontend unit tests.\n- `./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen` — regenerate the UI client after contract changes.\n\n## Cross-Module Rules\n\n- Keep API contracts, backend handlers, and UI usage in sync; never hand-edit `ui/packages/api-client/src/` — regenerate it after contract changes.\n- Versions live in `gradle/libs.versions.toml`, `gradle.properties`, or `ui/package.json`; never hard-code them.\n- Branch naming: `feat/`, `fix/`, `improvement/`, `upgrade/`; PRs target `upstream` and follow `.github/pull_request_template.md`.\n- Stage specific files only; never `git add -A` (H2 artifacts and build outputs appear under `application/`).\n- Add tests for changed behavior; keep CI green.\n- Ask before public API changes, new dependencies, database migrations, security configuration, or CI workflow changes.\n- Never commit secrets or introduce blocking I/O into reactive flows.\n\n<!-- File: api/AGENTS.md (Tokens: ~246 | Category: root-instruction) -->\n# API Library Guidelines\n\n`api/` is the public API library: the extension model, service contracts, and security abstractions consumed by the application and by plugins. Changes here affect downstream compatibility.\n\n## Scope\n\n- Source: `api/src/main/java/run/halo/app/` (core, extension, plugin, security, migration, theme, and related packages).\n- Tests: `api/src/test/`.\n\n## Commands\n\n- `./gradlew :api:test` — run API library tests.\n- `./gradlew spotlessApply` — format Java and Markdown.\n\n## Conventions\n\n- Java 21, 4-space indentation, formatted with Spotless.\n- The API is a compatibility surface: prefer additive changes, and flag any breaking signature or contract change for review before merging.\n- Extension definitions and schemas live in `application/src/main/resources/extensions/`; when you change them, update `application/` and `ui/` together.\n- Tests: JUnit 5, named `XxxTest` (unit) or `XxxIntegrationTest` (integration).\n\n## Boundaries\n\n- Public API changes in `api/` require explicit approval — treat them as affecting every plugin built against Halo.\n\n<!-- File: application/AGENTS.md (Tokens: ~347 | Category: root-instruction) -->\n# Backend Application Guidelines\n\n`application/` is the WebFlux + R2DBC backend: services, routers, security, migrations, and packaging. Everything here is reactive — never block threads.\n\n## Structure\n\n- `application/src/main/java/` — services, routers, endpoints, and configuration.\n- `application/src/main/resources/application*.yaml` — environment configuration.\n- `application/src/main/resources/db/migration/{h2,mariadb,mysql,postgresql}/` — per-dialect migrations.\n- `application/src/main/resources/extensions/` — extension schemas and role templates.\n- `application/src/test/` — backend tests.\n\n## Commands\n\n- `./gradlew :application:test` — run backend tests.\n- `./gradlew :application:bootRun` — start the backend dev server.\n- `./gradlew :application:copyUiDist` — embed the built UI into the JAR.\n- `./gradlew generateOpenApiDocs` — emit the OpenAPI spec used to regenerate the UI client.\n\n## Conventions\n\n- Keep reactive flows non-blocking; no blocking I/O in request paths.\n- Database migrations are versioned per dialect — never auto-generate them, and review them with the `application/` guide in mind.\n- Tests: JUnit 5 with Spring Boot test support; name unit tests `XxxTest` and integration tests `XxxIntegrationTest`.\n- After DTO, route, or schema changes, regenerate the UI client (`./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen`) and update `ui/` usage.\n\n## Boundaries\n\n- Ask before adding database migrations, changing security configuration, or introducing new dependencies.\n\n<!-- File: ui/AGENTS.md (Tokens: ~487 | Category: root-instruction) -->\n# Frontend Guidelines\n\n`ui/` is the Vue 3 + TypeScript + TailwindCSS frontend, managed with pnpm workspaces.\n\n## Structure\n\n- `ui/console-src/` — console application (entry: `console.html`, alias `@console`).\n- `ui/uc-src/` — user center application (entry: `uc.html`, alias `@uc`).\n- `ui/src/` — shared code: build-time Vite helpers (`src/vite/`), locales, assets, and shared views (alias `@`); not an application entry point.\n- `ui/packages/` — shared workspace packages (`@halo-dev/api-client`, `@halo-dev/components`, `@halo-dev/console-shared`, `@halo-dev/editor`, `@halo-dev/shared`, `@halo-dev/ui-plugin-bundler-kit`).\n- Unit tests: `*.spec.ts` files, usually under `__tests__/` next to the code they cover.\n\n## Commands\n\nRun from the repo root:\n\n- `pnpm -C ui install` / `pnpm -C ui dev` — install dependencies / run the dev server.\n- `pnpm -C ui build` — production build.\n- `pnpm -C ui typecheck` — `vue-tsc` type checking.\n- `pnpm -C ui lint` — ESLint (fails on any warning).\n- `pnpm -C ui format` / `pnpm -C ui format:check` — Prettier formatting.\n- `pnpm -C ui test:unit` — Vitest unit tests.\n- `./gradlew generateOpenApiDocs && pnpm -C ui api-client:gen` — regenerate `ui/packages/api-client` after backend contract changes.\n\n## Conventions\n\n- Follow existing component and composable patterns; match surrounding code rather than introducing new stylistic islands.\n- Never hand-edit `ui/packages/api-client/src/` — it is generated; regenerate it instead.\n- Keep application dependency versions in `ui/package.json`. For dependencies shared by workspace packages, define versions in the catalog in `ui/pnpm-workspace.yaml` and reference them with `catalog:`; never duplicate version declarations across packages.\n- Payload or UX changes usually ship together with the backend change that defines the contract.\n\n## Boundaries\n\n- New npm dependencies require approval.\n\n<!-- File: api/CLAUDE.md (Tokens: ~5 | Category: claude-rule) -->\n@AGENTS.md\n\n<!-- File: application/CLAUDE.md (Tokens: ~5 | Category: claude-rule) -->\n@AGENTS.md\n\n<!-- File: CLAUDE.md (Tokens: ~5 | Category: claude-rule) -->\n@AGENTS.md\n\n<!-- File: ui/CLAUDE.md (Tokens: ~5 | Category: claude-rule) -->\n@AGENTS.md\n\n<!-- File: .claude/skills/gradle-dependency-updates/SKILL.md (Tokens: ~732 | Category: anthropic-skill) -->\n---\nname: gradle-dependency-updates\ndescription: >-\n  Upgrade outdated Gradle dependencies in this project. Use this skill whenever\n  the user asks to check for dependency updates, show what's outdated, or\n  upgrade/bump/update Gradle dependencies, libraries, or plugins — including\n  mentions of \"dependencyUpdates\", the versions plugin, or \"outdated\n  dependencies\". Backend only (api/application/platform); frontend dependencies\n  (ui/, pnpm) are out of scope.\n---\n\n# Gradle Dependency Updates\n\nThis project manages backend dependencies through a Gradle version catalog\n(`gradle/libs.versions.toml`) plus the Spring Boot BOM. The ben-manes Versions\nplugin is already applied to `:api` and `:application`.\n\n## 1. Find what's outdated\n\n```bash\n./gradlew dependencyUpdates -DoutputFormatter=plain,json --console=plain\n```\n\nRead the reports at `api/build/dependencyUpdates/report.{txt,json}` and\n`application/build/dependencyUpdates/report.{txt,json}`; merge and dedupe\nacross the two modules.\n\nThe plugin's default revision includes stable releases, so \"later milestone\nversions\" does not mean pre-releases — only versions with a qualifier\n(alpha/beta/rc) are pre-releases.\n\n## 2. Decide what can be bumped\n\n- **Catalog library** — declared in `[libraries]` of `gradle/libs.versions.toml`\n  → bumpable in the catalog.\n- **Gradle plugin** — declared in `[plugins]` → bumpable in the catalog.\n- **BOM-managed** — pinned by the Spring Boot BOM in `platform/application`\n  (Spring Framework, r2dbc drivers, postgresql, byte-buddy, jspecify,\n  junit-platform-launcher, ...) → never bump individually; they arrive with the\n  next Spring Boot BOM bump.\n- **Gradle wrapper** — `gradle/wrapper/gradle-wrapper.properties`.\n\n## 3. Project constraints (do not cross)\n\n- **thymeleaf**: the `runtimeOnly ':thymeleaf:...'` flat-dir jars in\n  `application/build.gradle` are a pinned workaround (halo-dev/halo#7289) —\n  never touch them.\n- **lombok**: keep aligned with the `io.freefair.lombok` plugin; do not add a\n  `lombok { version = ... }` override.\n- **ben-manes Versions plugin itself**: stay on 0.54.0 — 0.59.0 fails to apply\n  with a classloader error.\n- **Pre-releases** (e.g. tika-core 4.0.0-beta-1): skip unless the user asks.\n- Versions live only in `gradle/libs.versions.toml`, `gradle.properties`, or\n  `ui/package.json` — never hard-code them (AGENTS.md).\n\n## 4. Show the update plan\n\nGroup by category, current → latest with the semver delta (major/minor/patch),\nstate where each bump lands, and list excluded items with reasons.\n\n## 5. Apply and verify\n\nEdit `gradle/libs.versions.toml` — one line per bump — only after the user\napproves the plan, then confirm the build:\n\n```bash\n./gradlew build\n```\n\nRe-run `dependencyUpdates` and check that the bumped items no longer appear as\noutdated; what remains should be exactly the out-of-scope set.\n\n<!-- File: .claude/skills/openspec-apply-change/SKILL.md (Tokens: ~1276 | Category: anthropic-skill) -->\n---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n\n<!-- File: .claude/skills/openspec-archive-change/SKILL.md (Tokens: ~1147 | Category: anthropic-skill) -->\n---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n\n<!-- File: .claude/skills/openspec-explore/SKILL.md (Tokens: ~2327 | Category: anthropic-skill) -->\n---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n\n<!-- File: .claude/skills/openspec-propose/SKILL.md (Tokens: ~1231 | Category: anthropic-skill) -->\n---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n\n<!-- File: .claude/skills/openspec-sync-specs/SKILL.md (Tokens: ~1184 | Category: anthropic-skill) -->\n---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n\n<!-- File: .claude/skills/openspec-update-change/SKILL.md (Tokens: ~1305 | Category: anthropic-skill) -->\n---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n\n<!-- File: .codex/skills/openspec-apply-change/SKILL.md (Tokens: ~1276 | Category: anthropic-skill) -->\n---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n\n<!-- File: .codex/skills/openspec-archive-change/SKILL.md (Tokens: ~1147 | Category: anthropic-skill) -->\n---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n\n<!-- File: .codex/skills/openspec-explore/SKILL.md (Tokens: ~2327 | Category: anthropic-skill) -->\n---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n\n<!-- File: .codex/skills/openspec-propose/SKILL.md (Tokens: ~1231 | Category: anthropic-skill) -->\n---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n\n<!-- File: .codex/skills/openspec-sync-specs/SKILL.md (Tokens: ~1184 | Category: anthropic-skill) -->\n---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n\n<!-- File: .codex/skills/openspec-update-change/SKILL.md (Tokens: ~1305 | Category: anthropic-skill) -->\n---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n\n<!-- File: .github/skills/openspec-apply-change/SKILL.md (Tokens: ~1276 | Category: anthropic-skill) -->\n---\nname: openspec-apply-change\ndescription: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using openspec-continue-change\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! Ready to archive this change.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n\n<!-- File: .github/skills/openspec-archive-change/SKILL.md (Tokens: ~1147 | Category: anthropic-skill) -->\n---\nname: openspec-archive-change\ndescription: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nArchive a completed change in the experimental workflow.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show only active changes (not already archived).\n   Include the schema used for each change if available.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Check artifact completion status**\n\n   Run `openspec status --change \"<name>\" --json` to check artifact completion.\n\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context\n   - `artifacts`: List of artifacts with their status (`done` or other)\n\n   **If any artifacts are not `done`:**\n   - Display warning listing incomplete artifacts\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n3. **Check task completion status**\n\n   Read the tasks file (typically `tasks.md`) to check for incomplete tasks.\n\n   Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).\n\n   **If incomplete tasks found:**\n   - Display warning showing count of incomplete tasks\n   - Use **AskUserQuestion tool** to confirm user wants to proceed\n   - Proceed if user confirms\n\n   **If no tasks file exists:** Proceed without task-related warning.\n\n4. **Assess delta spec sync state**\n\n   Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.\n\n   **If delta specs exist:**\n   - Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`\n   - Determine what changes would be applied (adds, modifications, removals, renames)\n   - Show a combined summary before prompting\n\n   **Prompt options:**\n   - If changes needed: \"Sync now (recommended)\", \"Archive without syncing\"\n   - If already synced: \"Archive now\", \"Sync anyway\", \"Cancel\"\n\n   If user chooses sync, use Task tool (subagent_type: \"general-purpose\", prompt: \"Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>\"). Proceed to archive regardless of choice.\n\n5. **Perform the archive**\n\n   Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:\n   ```bash\n   mkdir -p \"<planningHome.changesDir>/archive\"\n   ```\n\n   Generate target name using current date: `YYYY-MM-DD-<change-name>`\n\n   **Check if target already exists:**\n   - If yes: Fail with error, suggest renaming existing archive or using different date\n   - If no: Move `changeRoot` to the archive directory\n\n   ```bash\n   mv \"<changeRoot>\" \"<planningHome.changesDir>/archive/YYYY-MM-DD-<name>\"\n   ```\n\n6. **Display summary**\n\n   Show archive completion summary including:\n   - Change name\n   - Schema that was used\n   - Archive location\n   - Whether specs were synced (if applicable)\n   - Note about any warnings (incomplete artifacts/tasks)\n\n**Output On Success**\n\n```\n## Archive Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/\n**Specs:** ✓ Synced to main specs (or \"No delta specs\" or \"Sync skipped\")\n\nAll artifacts complete. All tasks complete.\n```\n\n**Guardrails**\n- Always prompt for change selection if not provided\n- Use artifact graph (openspec status --json) for completion checking\n- Don't block archive on warnings - just inform and confirm\n- Preserve .openspec.yaml when moving to archive (it moves with the directory)\n- Show clear summary of what happened\n- If sync is requested, use openspec-sync-specs approach (agent-driven)\n- If delta specs exist, always run the sync assessment and show the combined summary before prompting\n\n<!-- File: .github/skills/openspec-explore/SKILL.md (Tokens: ~2327 | Category: anthropic-skill) -->\n---\nname: openspec-explore\ndescription: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nEnter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.\n\n**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.\n\n**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.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n---\n\n## The Stance\n\n- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script\n- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.\n- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking\n- **Adaptive** - Follow interesting threads, pivot when new information emerges\n- **Patient** - Don't rush to conclusions, let the shape of the problem emerge\n- **Grounded** - Explore the actual codebase when relevant, don't just theorize\n\n---\n\n## What You Might Do\n\nDepending on what the user brings, you might:\n\n**Explore the problem space**\n- Ask clarifying questions that emerge from what they said\n- Challenge assumptions\n- Reframe the problem\n- Find analogies\n\n**Investigate the codebase**\n- Map existing architecture relevant to the discussion\n- Find integration points\n- Identify patterns already in use\n- Surface hidden complexity\n\n**Compare options**\n- Brainstorm multiple approaches\n- Build comparison tables\n- Sketch tradeoffs\n- Recommend a path (if asked)\n\n**Visualize**\n```\n┌─────────────────────────────────────────┐\n│     Use ASCII diagrams liberally        │\n├─────────────────────────────────────────┤\n│                                         │\n│      ┌────────┐         ┌────────┐      │\n│      │ State  │────────▶│ State  │      │\n│      │   A    │         │   B    │      │\n│      └────────┘         └────────┘      │\n│                                         │\n│   System diagrams, state machines,      │\n│   data flows, architecture sketches,    │\n│   dependency graphs, comparison tables  │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n**Surface risks and unknowns**\n- Identify what could go wrong\n- Find gaps in understanding\n- Suggest spikes or investigations\n\n---\n\n## OpenSpec Awareness\n\nYou have full context of the OpenSpec system. Use it naturally, don't force it.\n\n### Check for context\n\nAt the start, quickly check what exists:\n```bash\nopenspec list --json\n```\n\nThis tells you:\n- If there are active changes\n- Their names, schemas, and status\n- What the user might be working on\n\n### When no change exists\n\nThink freely. When insights crystallize, you might offer:\n\n- \"This feels solid enough to start a change. Want me to create a proposal?\"\n- Or keep exploring - no pressure to formalize\n\n### When a change exists\n\nIf the user mentions a change or you detect one is relevant:\n\n1. **Resolve and read existing artifacts for context**\n   - Run `openspec status --change \"<name>\" --json`.\n   - Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.\n   - Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.\n\n2. **Reference them naturally in conversation**\n   - \"Your design mentions using Redis, but we just realized SQLite fits better...\"\n   - \"The proposal scopes this to premium users, but we're now thinking everyone...\"\n\n3. **Offer to capture when decisions are made**\n\n    | Insight Type               | Where to Capture               |\n    |----------------------------|--------------------------------|\n    | New requirement discovered | `specs/<capability>/spec.md` |\n    | Requirement changed        | `specs/<capability>/spec.md` |\n    | Design decision made       | `design.md`                  |\n    | Scope changed              | `proposal.md`                |\n    | New work identified        | `tasks.md`                   |\n    | Assumption invalidated     | Relevant artifact              |\n\n   Example offers:\n   - \"That's a design decision. Capture it in design.md?\"\n   - \"This is a new requirement. Add it to specs?\"\n   - \"This changes scope. Update the proposal?\"\n\n4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.\n\n---\n\n## What You Don't Have To Do\n\n- Follow a script\n- Ask the same questions every time\n- Produce a specific artifact\n- Reach a conclusion\n- Stay on topic if a tangent is valuable\n- Be brief (this is thinking time)\n\n---\n\n## Handling Different Entry Points\n\n**User brings a vague idea:**\n```\nUser: I'm thinking about adding real-time collaboration\n\nYou: Real-time collab is a big space. Let me think about this...\n\n      COLLABORATION SPECTRUM\n      ════════════════════════════════════════════\n\n      Awareness          Coordination         Sync\n          │                   │                 │\n          ▼                   ▼                 ▼\n      ┌────────┐         ┌────────┐        ┌────────┐\n      │Presence│         │Cursors │        │  CRDT  │\n      │  \"3    │         │ Multi  │        │Conflict│\n      │online\" │         │ select │        │  free  │\n      └────────┘         └────────┘        └────────┘\n          │                   │                 │\n       trivial            moderate           complex\n\n      Where's your head at?\n```\n\n**User brings a specific problem:**\n```\nUser: The auth system is a mess\n\nYou: [reads codebase]\n\n     ┌─────────────────────────────────────────────┐\n     │              CURRENT AUTH FLOW              │\n     └─────────────────────────────────────────────┘\n                        │\n          ┌─────────────┼─────────────┐\n          ▼             ▼             ▼\n     ┌─────────┐  ┌─────────┐  ┌─────────┐\n     │ Google  │  │  GitHub │  │  Email  │\n     │  OAuth  │  │  OAuth  │  │  Magic  │\n     └────┬────┘  └────┬────┘  └────┬────┘\n          │             │             │\n          └─────────────┼─────────────┘\n                        ▼\n                  ┌───────────┐\n                  │  Session  │\n                  └─────┬─────┘\n                        │\n                        ▼\n                  ┌───────────┐\n                  │   Perms   │\n                  └───────────┘\n\n     I see three tangles. Which one's burning?\n```\n\n**User is stuck mid-implementation:**\n```\nUser: /opsx:explore add-auth-system\n      The OAuth integration is more complex than expected\n\nYou: [reads change artifacts]\n\n     You're on task 4: \"Implement OAuth flow\"\n\n     Let me trace what's involved...\n\n     [draws diagram, explores options, suggests paths]\n\n     Want to update the design to reflect this?\n     Or add a spike task to investigate?\n```\n\n**User wants to compare options:**\n```\nUser: Should we use Postgres or SQLite?\n\nYou: Generic answer is boring. What's the context?\n\nUser: A CLI tool that tracks local dev environments\n\nYou: That changes everything.\n\n     ┌─────────────────────────────────────────────────┐\n     │          CLI TOOL DATA STORAGE                  │\n     └─────────────────────────────────────────────────┘\n\n     Key constraints:\n     • No daemon running\n     • Must work offline\n     • Single user\n\n                  SQLite          Postgres\n     Deployment   embedded ✓      needs server ✗\n     Offline      yes ✓           no ✗\n     Single file  yes ✓           no ✗\n\n     SQLite. Not even close.\n\n     Unless... is there a sync component?\n```\n\n---\n\n## Ending Discovery\n\nThere's no required ending. Discovery might:\n\n- **Flow into a proposal**: \"Ready to start? I can create a change proposal.\"\n- **Result in artifact updates**: \"Updated design.md with these decisions\"\n- **Just provide clarity**: User has what they need, moves on\n- **Continue later**: \"We can pick this up anytime\"\n\nWhen it feels like things are crystallizing, you might summarize:\n\n```\n## What We Figured Out\n\n**The problem**: [crystallized understanding]\n\n**The approach**: [if one emerged]\n\n**Open questions**: [if any remain]\n\n**Next steps** (if ready):\n- Create a change proposal\n- Keep exploring: just keep talking\n```\n\nBut this summary is optional. Sometimes the thinking IS the value.\n\n---\n\n## Guardrails\n\n- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.\n- **Don't fake understanding** - If something is unclear, dig deeper\n- **Don't rush** - Discovery is thinking time, not task time\n- **Don't force structure** - Let patterns emerge naturally\n- **Don't auto-capture** - Offer to save insights, don't just do it\n- **Do visualize** - A good diagram is worth many paragraphs\n- **Do explore the codebase** - Ground discussions in reality\n- **Do question assumptions** - Including the user's and your own\n\n<!-- File: .github/skills/openspec-propose/SKILL.md (Tokens: ~1231 | Category: anthropic-skill) -->\n---\nname: openspec-propose\ndescription: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nPropose a new change - create the change and generate all artifacts in one step.\n\nI'll create a change with artifacts:\n- proposal.md (what & why)\n- design.md (how)\n- tasks.md (implementation steps)\n\nWhen ready to implement, run /opsx:apply\n\n---\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.\n\n**Steps**\n\n1. **If no clear input provided, ask what they want to build**\n\n   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:\n   > \"What change do you want to work on? Describe what you want to build or fix.\"\n\n   From their description, derive a kebab-case name (e.g., \"add user authentication\" → `add-user-auth`).\n\n   **IMPORTANT**: Do NOT proceed without understanding what the user wants to build.\n\n2. **Create the change directory**\n   ```bash\n   openspec new change \"<name>\"\n   ```\n   This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.\n\n3. **Get the artifact build order**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to get:\n   - `applyRequires`: array of artifact IDs needed before implementation (e.g., `[\"tasks\"]`)\n   - `artifacts`: list of all artifacts with their status and dependencies\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n4. **Create artifacts in sequence until apply-ready**\n\n   Use the **TodoWrite tool** to track progress through the artifacts.\n\n   Loop through artifacts in dependency order (artifacts with no pending dependencies first):\n\n   a. **For each artifact that is `ready` (dependencies satisfied)**:\n      - Get instructions:\n        ```bash\n        openspec instructions <artifact-id> --change \"<name>\" --json\n        ```\n      - The instructions JSON includes:\n        - `context`: Project background (constraints for you - do NOT include in output)\n        - `rules`: Artifact-specific rules (constraints for you - do NOT include in output)\n        - `template`: The structure to use for your output file\n        - `instruction`: Schema-specific guidance for this artifact type\n        - `resolvedOutputPath`: Resolved path or pattern to write the artifact\n        - `dependencies`: Completed artifacts to read for context\n      - Read any completed dependency files for context\n      - Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`\n      - Apply `context` and `rules` as constraints - but do NOT copy them into the file\n      - Show brief progress: \"Created <artifact-id>\"\n\n   b. **Continue until all `applyRequires` artifacts are complete**\n      - After creating each artifact, re-run `openspec status --change \"<name>\" --json`\n      - Check if every artifact ID in `applyRequires` has `status: \"done\"` in the artifacts array\n      - Stop when all `applyRequires` artifacts are done\n\n   c. **If an artifact requires user input** (unclear context):\n      - Use **AskUserQuestion tool** to clarify\n      - Then continue with creation\n\n5. **Show final status**\n   ```bash\n   openspec status --change \"<name>\"\n   ```\n\n**Output**\n\nAfter completing all artifacts, summarize:\n- Change name and location\n- List of artifacts created with brief descriptions\n- What's ready: \"All artifacts created! Ready for implementation.\"\n- Prompt: \"Run `/opsx:apply` or ask me to implement to start working on the tasks.\"\n\n**Artifact Creation Guidelines**\n\n- Follow the `instruction` field from `openspec instructions` for each artifact type\n- The schema defines what each artifact should contain - follow it\n- Read dependency artifacts for context before creating new ones\n- Use `template` as the structure for your output file - fill in its sections\n- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file\n  - Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact\n  - These guide what you write, but should never appear in the output\n\n**Guardrails**\n- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)\n- Always read dependency artifacts before creating a new one\n- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum\n- If a change with that name already exists, ask if user wants to continue it or create a new one\n- Verify each artifact file exists after writing before proceeding to next\n\n<!-- File: .github/skills/openspec-sync-specs/SKILL.md (Tokens: ~1184 | Category: anthropic-skill) -->\n---\nname: openspec-sync-specs\ndescription: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nSync delta specs from a change to main specs.\n\nThis 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).\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.\n\n   Show changes that have delta specs (under `specs/` directory).\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Resolve change context**\n\n   Run:\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n\n3. **Find delta specs**\n\n   Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.\n\n   Each delta spec file contains sections like:\n   - `## ADDED Requirements` - New requirements to add\n   - `## MODIFIED Requirements` - Changes to existing requirements\n   - `## REMOVED Requirements` - Requirements to remove\n   - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)\n\n   If no delta specs found, inform user and stop.\n\n4. **For each delta spec, apply changes to main specs**\n\n   For each repo-local capability delta spec path returned by the CLI:\n\n   a. **Read the delta spec** to understand the intended changes\n\n   b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)\n\n   c. **Apply changes intelligently**:\n\n      **ADDED Requirements:**\n      - If requirement doesn't exist in main spec → add it\n      - If requirement already exists → update it to match (treat as implicit MODIFIED)\n\n      **MODIFIED Requirements:**\n      - Find the requirement in main spec\n      - Apply the changes - this can be:\n        - Adding new scenarios (don't need to copy existing ones)\n        - Modifying existing scenarios\n        - Changing the requirement description\n      - Preserve scenarios/content not mentioned in the delta\n\n      **REMOVED Requirements:**\n      - Remove the entire requirement block from main spec\n\n      **RENAMED Requirements:**\n      - Find the FROM requirement, rename to TO\n\n   d. **Create new main spec** if capability doesn't exist yet:\n      - Create `openspec/specs/<capability>/spec.md`\n      - Add Purpose section (can be brief, mark as TBD)\n      - Add Requirements section with the ADDED requirements\n\n5. **Show summary**\n\n   After applying all changes, summarize:\n   - Which capabilities were updated\n   - What changes were made (requirements added/modified/removed/renamed)\n\n**Delta Spec Format Reference**\n\n```markdown\n## ADDED Requirements\n\n### Requirement: New Feature\nThe system SHALL do something new.\n\n#### Scenario: Basic case\n- **WHEN** user does X\n- **THEN** system does Y\n\n## MODIFIED Requirements\n\n### Requirement: Existing Feature\n#### Scenario: New scenario to add\n- **WHEN** user does A\n- **THEN** system does B\n\n## REMOVED Requirements\n\n### Requirement: Deprecated Feature\n\n## RENAMED Requirements\n\n- FROM: `### Requirement: Old Name`\n- TO: `### Requirement: New Name`\n```\n\n**Key Principle: Intelligent Merging**\n\nUnlike programmatic merging, you can apply **partial updates**:\n- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios\n- The delta represents *intent*, not a wholesale replacement\n- Use your judgment to merge changes sensibly\n\n**Output On Success**\n\n```\n## Specs Synced: <change-name>\n\nUpdated main specs:\n\n**<capability-1>**:\n- Added requirement: \"New Feature\"\n- Modified requirement: \"Existing Feature\" (added 1 scenario)\n\n**<capability-2>**:\n- Created new spec file\n- Added requirement: \"Another Feature\"\n\nMain specs are now updated. The change remains active - archive when implementation is complete.\n```\n\n**Guardrails**\n- Read both delta and main specs before making changes\n- Preserve existing content not mentioned in delta\n- If something is unclear, ask for clarification\n- Show what you're changing as you go\n- The operation should be idempotent - running twice should give same result\n\n<!-- File: .github/skills/openspec-update-change/SKILL.md (Tokens: ~1305 | Category: anthropic-skill) -->\n---\nname: openspec-update-change\ndescription: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.\nallowed-tools: Bash(openspec:*)\nlicense: MIT\ncompatibility: Requires openspec CLI.\nmetadata:\n  author: openspec\n  version: \"1.0\"\n  generatedBy: \"1.6.0\"\n---\n\nRevise a change's existing planning artifacts and keep them coherent. Never edit code.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.\n\n**Steps**\n\n1. **If no change name provided, prompt for selection**\n\n   Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.\n\n   Present the top 3-4 most recently modified changes as options, showing:\n   - Change name\n   - Schema (from `schema` field if present, otherwise \"spec-driven\")\n   - Status (e.g., \"0/5 tasks\", \"complete\", \"no tasks\")\n   - How recently it was modified (from `lastModified` field)\n\n   Mark the most recently modified change as \"(Recommended)\" since it's likely what the user wants to update.\n\n   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.\n\n2. **Get the change's artifacts**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand current state. The response includes:\n   - `schemaName`: The workflow schema being used (e.g., \"spec-driven\")\n   - `artifacts`: Array of artifacts with their status (\"done\", \"ready\", \"blocked\")\n   - `isComplete`: Boolean indicating if all artifacts are complete\n   - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.\n\n   The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.\n\n   The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.\n\n3. **Understand the request**\n   - If the user asked for a specific revision (\"the design now uses X\"), that is the starting edit.\n   - If they only said \"update\" / \"make this coherent\", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.\n\n4. **Read and reconcile**\n   - Read the artifact(s) the request touches and the change's other existing artifacts.\n   - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.\n   - Note everything that is now inconsistent, missing, or contradictory.\n   - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.\n   - If the change is already coherent, say so and make no edits.\n\n5. **Confirm and apply, one artifact at a time**\n   - Show each proposed revision and why. Write only after the user confirms.\n   - If the user rejects a revision, do not write it - leave that artifact unchanged.\n   - When a substantial rewrite is needed, get that artifact's rules and template first:\n     ```bash\n     openspec instructions <artifact-id> --change \"<name>\" --json\n     ```\n\n6. **Point to the next step (guidance only - NEVER act on it)**\n   - Artifacts still missing -> suggest `/opsx:continue` to create them.\n   - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.\n   - Everything done and implemented -> suggest `/opsx:archive`.\n\n**Output**\n\nAfter each invocation, show:\n- Which artifacts were revised (and which proposed revisions were rejected)\n- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)\n- Where the change stands and the recommended next command\n\n**Guardrails**\n- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.\n- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.\n- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.\n- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.\n- Confirm every edit with the user before writing.\n- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the \"Update vs. Start Fresh\" heuristic).\n\n<!-- File: ui/packages/editor/docs/extension.md (Tokens: ~7291 | Category: anthropic-skill) -->\n# 扩展说明\n\n本文档介绍如何对编辑器的功能进行扩展，包括但不限于扩展工具栏、悬浮工具栏、Slash Command、拖拽功能等。各扩展区域参考下图：\n\n![编辑器扩展说明](extension.png)\n\n目前支持的所有扩展类型 [ExtensionOptions](../packages/editor/src/types/index.ts) 如下所示：\n\n```ts\nexport interface ExtensionOptions {\n  // 顶部工具栏扩展\n  getToolbarItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => ToolbarItemType | ToolbarItemType[];\n\n  // Slash Command 扩展\n  getCommandMenuItems?: () => CommandMenuItemType | CommandMenuItemType[];\n\n  // 悬浮菜单扩展\n  getBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenuType;\n\n  // 工具箱扩展\n  getToolboxItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => ToolboxItemType | ToolboxItemType[];\n\n  // 拖拽菜单扩展\n  getDraggableMenuItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => DragButtonType | DragButtonType[];\n}\n```\n\n> 对于 Tiptap 本身的扩展方式可以参考 <https://tiptap.dev/api/introduction>\n\n## 1. 顶部工具栏扩展\n\n编辑器顶部功能区域内容的扩展，通常用于增加用户常用操作，例如文本加粗、变更颜色等。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/16> 中，我们实现了对顶部工具栏的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolbarItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolbarItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getToolbarItems` 即为对顶部工具栏的扩展。其返回类型为：\n\n```ts\n// 顶部工具栏扩展\ngetToolbarItems?: ({\n  editor,\n}: {\n  editor: Editor;\n}) => ToolbarItemType | ToolbarItemType[];\n\n// 工具栏\nexport interface ToolbarItemType {\n  priority: number;\n  component: Component;\n  props: {\n    editor: Editor;\n    isActive: boolean;\n    disabled?: boolean;\n    icon?: Component;\n    title?: string;\n    shortcutId?: string;\n    shortcutIds?: string[];\n    action?: () => void;\n  };\n  children?: ToolbarItemType[];\n}\n```\n\n如下为 [`Bold`](../packages/editor/src/extensions/bold/index.ts) 扩展中对于 `getToolbarItems` 的扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n    getToolbarItems({ editor }: { editor: Editor }) {\n      return {\n        priority: 40,\n        component: markRaw(ToolbarItem),\n        props: {\n          editor,\n          isActive: editor.isActive(\"bold\"),\n          icon: markRaw(MdiFormatBold),\n          title: i18n.global.t(\"editor.common.bold\"),\n          action: () => editor.chain().focus().toggleBold().run(),\n        },\n      };\n    },\n  };\n},\n```\n\n## 2. 快捷键与提示信息\n\nHalo 在 Tiptap 的 `addKeyboardShortcuts` 基础上提供了快捷键描述注册表。第三方扩展仍然只需要实现一个 `addKeyboardShortcuts`，同时即可获得以下能力：\n\n- 执行 Tiptap 快捷键命令；\n- 在“键盘快捷键”侧边栏中展示操作说明；\n- 通过同一个 `shortcutId` 在工具栏或悬浮菜单的 tooltip 中展示快捷键；\n- 根据当前操作系统将 `Mod`、`Alt` 等按键格式化为对应的展示形式。\n\n### 2.1 注册快捷键并关联工具栏\n\n使用 `defineHaloKeyboardShortcuts` 定义快捷键，再将相同的 `id` 传给工具栏组件的 `shortcutId`：\n\n```ts\nimport {\n  defineHaloKeyboardShortcuts,\n  Extension,\n  ToolbarItem,\n  type Editor,\n  type ExtensionOptions,\n} from \"@halo-dev/richtext-editor\";\nimport { markRaw } from \"vue\";\nimport MyIcon from \"./MyIcon.vue\";\n\nconst shortcutId = \"plugin.example.insertGreeting\";\n\nfunction insertGreeting(editor: Editor) {\n  return editor.chain().focus().insertContent(\"Hello Halo\").run();\n}\n\nexport const ExtensionExample = Extension.create<ExtensionOptions>({\n  name: \"exampleShortcut\",\n\n  addKeyboardShortcuts() {\n    return defineHaloKeyboardShortcuts(this, [\n      {\n        id: shortcutId,\n        keys: [\"Mod-Alt-g\"],\n        label: \"插入问候语\",\n        category: \"general\",\n        priority: 100,\n        command: () => insertGreeting(this.editor),\n      },\n    ]);\n  },\n\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolbarItems({ editor }: { editor: Editor }) {\n        return {\n          priority: 100,\n          component: markRaw(ToolbarItem),\n          props: {\n            editor,\n            isActive: false,\n            icon: markRaw(MyIcon),\n            title: \"插入问候语\",\n            shortcutId,\n            action: () => insertGreeting(editor),\n          },\n        };\n      },\n    };\n  },\n});\n```\n\n`ToolbarItem`、`ToolbarSubItem` 和 `BubbleItem` 都支持 `shortcutId`。`ToolbarItem` 还支持 `shortcutIds`，适用于一个按钮对应多个操作的情况，例如同时展示“增大字号”和“减小字号”。tooltip 会展示每个快捷键定义中的第一组按键，快捷键侧边栏则会展示 `keys` 中的全部可选按键。\n\n默认编辑器已经通过 `ExtensionsKit` 内置 `ExtensionKeyboardShortcuts`，插件通过 `default:editor:extension:create` 扩展点注册时无需重复添加。自行创建编辑器实例时，应使用 `ExtensionsKit`，或显式加入 `ExtensionKeyboardShortcuts`。\n\n### 2.2 描述字段\n\n`defineHaloKeyboardShortcuts` 接收的每一项都是一个 `HaloKeyboardShortcutDefinition`：\n\n| 字段 | 必填 | 说明 |\n| --- | --- | --- |\n| `id` | 是 | 编辑器内稳定且唯一的标识符，用于关联 tooltip。插件应使用包含插件标识的命名空间，例如 `plugin.example.insertGreeting`。 |\n| `keys` | 是 | Tiptap 格式的按键组合。第一项是 tooltip 展示的主快捷键，全部按键都会展示在快捷键侧边栏中。 |\n| `label` | 是 | 用户可见的操作名称，可以是字符串或返回字符串的函数。 |\n| `category` | 是 | 快捷键侧边栏分组：`general`、`formatting`、`structure` 或 `navigation`。 |\n| `command` | 视情况 | 新增快捷键时必须提供。扩展已有 Tiptap 快捷键时可以省略，此时复用父扩展中相同按键的命令。 |\n| `description` | 否 | 操作的补充说明，可以是字符串或返回字符串的函数。 |\n| `priority` | 否 | 在快捷键侧边栏同一分组中的排序值，数值越小越靠前，默认为 `100`。 |\n| `discoverable` | 否 | 是否出现在快捷键侧边栏中，默认为 `true`。即使设为 `false`，显式绑定了 `shortcutId` 的 tooltip 仍可展示。 |\n| `visible` | 否 | 根据当前编辑器状态决定是否出现在快捷键侧边栏中。 |\n\n按键名称遵循 [Tiptap 快捷键格式](https://tiptap.dev/docs/editor/core-concepts/keyboard-shortcuts)。建议使用 `Mod` 表示 macOS 的 `Command` 和 Windows/Linux 的 `Control`，例如 `Mod-b`。命令处理成功时应返回 `true`，这样 ProseMirror 会阻止浏览器继续执行同一按键的默认行为；未处理时应返回 `false`。\n\n如果扩展继承的 Tiptap 扩展已经实现了相同按键，可以只补充 Halo 的描述信息，不需要重新实现命令：\n\n```ts\naddKeyboardShortcuts() {\n  return defineHaloKeyboardShortcuts(this, [\n    {\n      id: \"plugin.example.toggleFeature\",\n      keys: [\"Mod-b\"],\n      label: \"切换示例功能\",\n      category: \"formatting\",\n    },\n  ]);\n},\n```\n\n只有父扩展确实定义了 `keys` 中对应的按键时才能省略 `command`。开发环境会对缺少实际命令的定义输出警告，并且不会注册这条描述。\n\n### 2.3 自定义组件中的 tooltip\n\n完全自定义工具栏组件时，可以通过 `useHaloKeyboardShortcut` 响应式读取注册表，再使用 `KeyboardShortcutTooltip` 保持与内置工具栏一致的视觉和无障碍信息：\n\n```vue\n<script setup lang=\"ts\">\nimport {\n  KeyboardShortcutTooltip,\n  useHaloKeyboardShortcut,\n  type Editor,\n} from \"@halo-dev/richtext-editor\";\n\nconst props = defineProps<{\n  editor: Editor;\n  shortcutId: string;\n  title: string;\n}>();\n\nconst shortcut = useHaloKeyboardShortcut(props.editor, () => props.shortcutId);\n</script>\n\n<template>\n  <KeyboardShortcutTooltip\n    v-slot=\"tooltipProps\"\n    :title=\"title\"\n    :shortcut=\"shortcut?.keys[0]\"\n  >\n    <button :aria-label=\"tooltipProps.ariaLabel\" type=\"button\">\n      {{ title }}\n    </button>\n  </KeyboardShortcutTooltip>\n</template>\n```\n\n一个组件需要读取多条快捷键时，可以使用 `useHaloKeyboardShortcuts(editor, () => shortcutIds)`。这两个 composable 必须在 Vue 组件的 `setup` 阶段调用，以便组件卸载时自动取消注册表订阅。\n\n### 2.4 命名与冲突规则\n\n- `id` 应包含插件标识，避免覆盖其他扩展注册的描述；快捷键注册表不会自动为重复 ID 添加命名空间。\n- 只注册产品中真实可执行的快捷键，不要为了填满快捷键侧边栏而自行创造按键组合。\n- 添加按键前应检查 Halo 默认快捷键、Tiptap 默认快捷键以及浏览器常用快捷键。确实需要覆盖浏览器默认行为时，命令必须在成功处理后返回 `true`。\n- `label` 和 `description` 应面向用户描述操作，不要使用内部命令名或扩展名。\n\n## 3. 工具箱扩展\n\n编辑器工具箱区域的扩展，可用于增加编辑器附属操作，例如插入表格，插入第三方组件等功能。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/27> 中，我们实现了对编辑器工具箱区域的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolboxItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getToolboxItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getToolboxItems` 即为对工具箱的扩展。其返回类型为：\n\n```ts\n// 工具箱扩展\ngetToolboxItems?: ({\n  editor,\n}: {\n  editor: Editor;\n}) => ToolboxItemType | ToolboxItemType[];\n\nexport interface ToolboxItemType {\n  priority: number;\n  component: Component;\n  props: {\n    editor: Editor;\n    icon?: Component;\n    title?: string;\n    description?: string;\n    action?: () => void;\n  };\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getToolboxItems` 工具箱的扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n    getToolboxItems({ editor }: { editor: Editor }) {\n      return {\n        priority: 15,\n        component: markRaw(ToolboxItem),\n        props: {\n          editor,\n          icon: markRaw(MdiTablePlus),\n          title: i18n.global.t(\"editor.menus.table.add\"),\n          action: () =>\n            editor\n              .chain()\n              .focus()\n              .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n              .run(),\n        },\n      };\n    },\n  }\n}\n```\n\n## 4. Slash Command 扩展\n\nSlash Command （斜杠命令）的扩展，可用于在当前行快捷执行功能操作，例如转换当前行为标题、在当前行添加代码块等功能。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/16> 中，我们实现了对 Slash Command 指令的扩展，如果需要添加额外的功能，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getCommandMenuItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getCommandMenuItems() {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getCommandMenuItems` 即为对工具箱的扩展。其返回类型为：\n\n```ts\n// Slash Command 扩展\ngetCommandMenuItems?: () => CommandMenuItemType | CommandMenuItemType[];\n\nexport interface CommandMenuItemType {\n  priority: number;\n  icon: Component;\n  title: string;\n  keywords: string[];\n  command: ({ editor, range }: { editor: Editor; range: Range }) => void;\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getCommandMenuItems` 的扩展示例：\n\n```ts\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getCommandMenuItems() {\n        return {\n          priority: 120,\n          icon: markRaw(MdiTable),\n          title: \"editor.extensions.commands_menu.table\",\n          keywords: [\"table\", \"biaoge\"],\n          command: ({ editor, range }: { editor: Editor; range: Range }) => {\n            editor\n              .chain()\n              .focus()\n              .deleteRange(range)\n              .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n              .run();\n          },\n        };\n      },\n    }\n  }\n```\n\n## 5. 悬浮菜单扩展\n\n编辑器悬浮菜单的扩展。可用于支持目标元素组件的功能扩展及操作简化。例如 `Table` 扩展中的添加下一列、添加上一列等操作。\n\n在 <https://github.com/halo-sigs/richtext-editor/pull/38> 中，我们重构了对编辑器悬浮区域的扩展，如果需要对某个块进行支持，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getBubbleMenu` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getBubbleMenu({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n其中 `getBubbleMenu` 即为对悬浮菜单的扩展。其返回类型为：\n\n```ts\n// 悬浮菜单扩展\ngetBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenuType;\n\ninterface BubbleMenuProps {\n  pluginKey?: string;                                             // 悬浮菜单插件 Key，建议命名方式 xxxBubbleMenu\n  editor?: Editor;\n  shouldShow: (props: {                                           // 悬浮菜单显示的条件\n    editor: Editor;\n    state: EditorState;\n    node?: HTMLElement;\n    view?: EditorView;\n    oldState?: EditorState;\n    from?: number;\n    to?: number;\n  }) => boolean;\n  tippyOptions?: Record<string, unknown>;                          // 可自由定制悬浮菜单所用的 tippy 组件的选项\n  getRenderContainer?: (node: HTMLElement) => HTMLElement;         // 悬浮菜单所基准的 DOM\n  defaultAnimation?: boolean;                                      // 是否启用默认动画。默认为 true\n}\n\n// 悬浮菜单\nexport interface NodeBubbleMenuType extends BubbleMenuProps {\n  component?: Component;                                           // 不使用默认的样式，与 items 二选一\n  items?: BubbleItemType[];                                       // 悬浮菜单子项，使用默认的形式进行，与 items 二选一\n  extendsKey?: string | PluginKey;                                 // 用于扩展已有悬浮菜单的 key，如果未提供，则会被视为一个新的悬浮菜单\n}\n\n// 悬浮菜单子项\nexport interface BubbleItemType {\n  priority: number;                                                // 优先级，数字越小优先级越大，越靠前\n  component?: Component;                                           // 完全自定义子项样式\n  key?: string;                                                    // 子项的唯一标识，通常用于扩展悬浮菜单时仅保留唯一的子项。\n  props?: {                                                        // 子项属性，可选。同时支持传入自定义属性\n    isActive?: ({ editor }: { editor: Editor }) => boolean;         // 当前功能是否已经处于活动状态\n    visible?: ({ editor }: { editor: Editor }) => boolean;         // 是否显示当前子项\n    icon?: Component;                                              // 图标\n    iconStyle?: string;                                            // 图标自定义样式\n    title?: string;                                                // 标题\n    action?: ({ editor }: { editor: Editor }) => Component | void; // 点击子项后的操作，如果返回 Component，则会将其包含在下拉框中。\n  } & Record<string, unknown>;\n}\n```\n\n如下为 [`Table`](../packages/editor/src/extensions/table/index.ts) 扩展中对于 `getBubbleMenu` 悬浮菜单的部分扩展示例：\n\n```ts\naddOptions() {\n  return {\n    ...this.parent?.(),\n      getBubbleMenu({ editor }) {\n        return {\n          pluginKey: \"tableBubbleMenu\",\n          shouldShow: ({ state }: { state: EditorState }): boolean => {\n            return isActive(state, Table.name);\n          },\n          getRenderContainer(node) {\n            let container = node;\n            if (container.nodeName === \"#text\") {\n              container = node.parentElement as HTMLElement;\n            }\n            while (\n              container &&\n              container.classList &&\n              !container.classList.contains(\"tableWrapper\")\n            ) {\n              container = container.parentElement as HTMLElement;\n            }\n            return container;\n          },\n          tippyOptions: {\n            offset: [26, 0],\n          },\n          items: [\n            {\n              priority: 10,\n              props: {\n                icon: markRaw(MdiTableColumnPlusBefore),\n                title: i18n.global.t(\"editor.menus.table.add_column_before\"),\n                action: () => editor.chain().focus().addColumnBefore().run(),\n              },\n            },\n          ]\n        }\n      }\n  }\n}\n```\n\n## 6. 拖拽菜单扩展\n\n拖拽菜单扩展主要用于拖拽的菜单功能扩展，例如转换为、复制、剪切、删除等操作。\n\n在 <https://github.com/halo-dev/halo/pull/7861> 中，我们重构了对编辑器拖拽区域的扩展，并且支持了对拖拽菜单的扩展。如果需要对拖拽菜单进行扩展，只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getDraggableMenuItems` 函数即可，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return []\n      },\n    };\n  },\n}\n```\n\n同时，为了支持不同扩展对同一菜单项的扩展，我们提供了 `extendsKey` 属性，用于指定扩展目标菜单项的唯一标识。只需将 `extendsKey` 设置为已有的菜单项的 `key`，即可扩展该菜单项。可扩展已有菜单项的 `visible`、`isActive`、`disabled`、`action` 方法以及 `children.items` 属性，如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return {\n          extendsKey: CONVERT_TO_KEY,\n          // 当任意扩展目标菜单项的 visible 方法返回 false 时，当前菜单项不会显示。返回 true 则会继续执行后续的扩展实现。\n          visible: ({ editor }) => {\n            if (isActive(editor.state, \"table\")) {\n              return false;\n            }\n            return true;\n          },\n        };\n      },\n    };\n  },\n};\n```\n\n拖拽菜单最多支持两级菜单嵌套， 如果想扩展已有的一级菜单，为其二级菜单增加内容，则需要同时设置 `extendsKey` 和 `children.items` 属性。如：\n\n```ts\n{\n  addOptions() {\n    return {\n      ...this.parent?.(),\n      getDraggableMenuItems({ editor }: { editor: Editor }) {\n        return {\n          extendsKey: CONVERT_TO_KEY,\n          children: {\n            items: [\n              {\n                priority: 10,\n                icon: markRaw(MdiFormatParagraph),\n                title: i18n.global.t(\"editor.common.heading.paragraph\"),\n                action: ({ editor }: { editor: Editor }) =>\n                  editor.chain().focus().setParagraph().run(),\n              },\n            ],\n          },\n        }\n      },\n    };\n  },\n}\n```\n\n默认情况下，将会追加 `items`，若想覆盖，则需要设置子菜单的 `key` 属性，将会覆盖原有的子菜单项。\n\n下面为 `getDraggableMenuItems` 的返回类型：\n\n```ts\n\n// 拖拽菜单扩展\ngetDraggableMenuItems?: ({\n    editor,\n  }: {\n    editor: Editor;\n  }) => DragButtonType | DragButtonType[];\n\n// 拖拽菜单项目属性\nexport interface DragButtonItemProps {\n  extendsKey?: string;                                    // 扩展目标菜单项的唯一标识，如果提供了该属性，则视为扩展目标菜单项。\n  key?: string;                                           // 唯一标识，如果同级菜单项设置了同样的 key，则会被合并为一个菜单项。\n  priority?: number;                                      // 优先级，数字越小优先级越大，越靠前\n  title?: string | (() => string);                        // 标题\n  icon?: Component;                                       // 图标\n  action?: ({                                             // 点击菜单后的操作，如果返回 Component，则会将其包含在子菜单中。\n                                                          // 可以通过调用 close 方法可以在操作完成后关闭拖拽菜单，或者当返回为 true 或 undefined 时，会自动关闭拖拽菜单，如果返回 false，则不会关闭拖拽菜单。\n                                                          // 多个扩展实现时，则按照顺序执行，并在返回非 undefined 值时停止执行。\n    editor,\n    node,\n    pos,\n    close,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n    close: () => void;\n  }) => Component | boolean | void | Promise<Component | boolean | void>;\n  iconStyle?: string;                                       // 图标自定义样式\n  class?: string;                                           // 自定义样式\n  visible?: ({                                              // 是否显示当前菜单项，默认为 true，多个扩展实现时，以 AND 逻辑判断，即所有扩展返回 true 时，当前菜单项才会显示。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  isActive?: ({                                             // 当前菜单项是否处于活动状态，默认为 false，多个扩展实现时，以 OR 逻辑判断，即只要有一个扩展返回 true，则当前菜单项处于活动状态。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  disabled?: ({                                                // 是否禁用当前菜单项，默认为 false，多个扩展实现时，以 OR 逻辑判断，即只要有一个扩展返回 true，则当前菜单项会被禁用。\n    editor,\n    node,\n    pos,\n  }: {\n    editor: Editor;\n    node: PMNode | null;\n    pos: number;\n  }) => boolean;\n  keyboard?: string;                                            // 快捷键，遵循 https://tiptap.dev/docs/editor/core-concepts/keyboard-shortcuts\n  component?: Component;                                        // 自定义组件，如果提供了该属性，则不会显示默认的菜单项，而是会显示自定义组件，并且将所有 props 传递给自定义组件。\n  [key: string]: any;                                           // 其他自定义属性，将会传递给自定义组件。\n}\n\n// 一级菜单项\nexport interface DragButtonType extends DragButtonItemProps {\n  children?: {                                                    // 子菜单项，如果提供了该属性，则视为扩展目标菜单项的二级菜单。\n    component?: Component;                                        // 自定义组件，如果提供了该属性，则不会显示默认的子菜单项，而是会显示自定义组件，并且将所有 props 传递给自定义组件。\n    items?: DragButtonItemProps[];                                // 子菜单项列表，如果提供了该属性，则视为扩展目标菜单项的二级菜单。\n  };\n}\n```\n\n## 7. 块缩进扩展\n\n编辑器会根据节点的 schema 元数据发现可缩进节点，不维护组件名称白名单。第三方节点只要属于 `block` group，且不属于 `list` group，就会自动获得块缩进属性、快捷键和拖拽缩进能力：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MyBlock = Node.create({\n  name: \"myBlock\",\n  group: \"block\",\n  // ...\n});\n```\n\n特殊节点可以通过 `haloEditorIndentation` 调整接入行为：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MyBlock = Node.create({\n  name: \"myBlock\",\n  group: \"block\",\n\n  // 光标在节点内部时，将 Tab / Shift-Tab 交给节点自身处理；\n  // 节点选中或光标位于节点左上角间隙时，仍可缩进整个节点。\n  haloEditorIndentation: {\n    keyboard: \"passthrough\",\n  },\n});\n```\n\n- 设置为 `false` 可以让 block 节点退出通用缩进。\n- 设置为 `true` 可以让不属于 block group 的特殊节点显式接入缩进。\n- `legacyLineIndent: true` 仅用于需要兼容旧版首行缩进数据的文本节点，不建议新扩展启用。\n\n自定义列表容器应加入 `list` group，列表项应加入 `listItem` group。编辑器通过这两个 schema group 识别列表层级、继承缩进和行内拖拽目标，不依赖 `bulletList`、`orderedList`、`listItem` 等具体节点名称。\n\n第三方块命令可以复用以下公共 helpers：\n\n```ts\nimport {\n  findAncestorListItems,\n  getBlockIndentAtSelection,\n  prepareBlockCommandFromList,\n} from \"@halo-dev/richtext-editor\";\n\nconst listItems = findAncestorListItems(editor.state.selection.$from);\nconst indent = getBlockIndentAtSelection(editor);\nconst preparedRange = prepareBlockCommandFromList(editor, range);\n```\n\n- `findAncestorListItems` 按 schema group 查找当前光标所在的列表项，结果从内层到外层排列。\n- `getBlockIndentAtSelection` 将显式块缩进和列表层级换算为当前配置下的可视缩进。\n- `prepareBlockCommandFromList` 适用于 Slash Command 一类块命令：移除触发文本、退出列表并保留可视缩进，且整个操作可以一次撤销。\n\n缩进步长、最小值、最大值和默认值都可以通过 `ExtensionsKit` 配置：\n\n```ts\nimport { ExtensionsKit } from \"@halo-dev/richtext-editor\";\n\nExtensionsKit.configure({\n  indent: {\n    indentRange: 32,\n    minIndentLevel: 0,\n    maxIndentLevel: 320,\n    defaultIndentLevel: 0,\n  },\n});\n```\n\n未配置 `maxIndentLevel` 时，默认允许 10 级缩进，并会随 `indentRange` 自动换算最大值。\n\n<!-- File: ui/packages/editor/docs/runtime-metadata.md (Tokens: ~1587 | Category: anthropic-skill) -->\n# 编辑器扩展的运行期元数据\n\n`@halo-dev/richtext-editor` 允许 Tiptap 的 Node、Mark 和 Extension\n声明运行期组件元数据，用于描述最终 Editor 实例中的 schema、组件用法、结构关系、\n属性和示例。这些信息从 Editor 当前注册的扩展及最终 schema 中同步解析；AI Agent\n是目前的主要消费者，但元数据本身是对运行期组件能力的描述，不会改变或约束组件行为。\n\n## 声明新组件\n\n下面的数学公式节点说明了适用场景、属性和生成所需的外部能力：\n\n```ts\nimport { Node } from \"@halo-dev/richtext-editor\";\n\nexport const MathBlock = Node.create({\n  name: \"mathBlock\",\n  group: \"block\",\n  atom: true,\n\n  addAttributes() {\n    return {\n      formula: {\n        default: \"\",\n      },\n    };\n  },\n\n  parseHTML() {\n    return [{ tag: 'div[data-type=\"math-block\"]' }];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    return [\"div\", { ...HTMLAttributes, \"data-type\": \"math-block\" }];\n  },\n\n  // 声明运行期元数据中的 AI 使用说明\n  addHaloEditorMetadata() {\n    return {\n      ai: {\n        description: \"A display mathematical formula.\",\n        exposure: \"available\",\n        useWhen: [\"Presenting a standalone mathematical expression.\"],\n        attributeGuidance: {\n          formula: {\n            description: \"Formula source written in LaTeX.\",\n            format: \"LaTeX\",\n            examples: [\"E = mc^2\"],\n          },\n        },\n        generation: {\n          mode: \"requires-capability\",\n          requiredCapabilities: [\"math-to-html\"],\n        },\n        examples: [\n          '<div data-type=\"math-block\" formula=\"E = mc^2\"></div>',\n        ],\n      },\n    };\n  },\n});\n```\n\nCapability 名称是开放字符串。Halo 只把它放入 Manifest，不负责查找或执行对应工具，由最终使用者扩展工具。\n\n## 扩展现有组件\n\n元数据与 Tiptap 的 `.extend()` 继承链一起自动合并。插件只需返回自己的局部补丁，\n不需要调用 `this.parent`，也不需要手动合并 Halo 已有说明：\n\n```ts\nimport { ExtensionCodeBlock } from \"@halo-dev/richtext-editor\";\n\nexport const HighlightedCodeBlock = ExtensionCodeBlock.extend({\n  addAttributes() {\n    return {\n      ...this.parent?.(),\n      highlightTheme: {\n        default: null,\n      },\n    };\n  },\n\n  addHaloEditorMetadata() {\n    return {\n      ai: {\n        attributeGuidance: {\n          highlightTheme: {\n            description: \"Syntax-highlighting theme.\",\n            allowedValues: [\"github-light\", \"github-dark\"],\n            omitWhen: [\"The editor default theme should be used.\"],\n          },\n        },\n      },\n    };\n  },\n});\n```\n\n最终 Manifest 会同时包含原 code block 的描述和 `highlightTheme`。\n\n## 为全局属性贡献说明\n\nPlain Extension 不会成为 Manifest 组件，但可向明确命名的 Node 或 Mark\n贡献元数据。这适合 `addGlobalAttributes()`：\n\n```ts\nimport { Extension } from \"@halo-dev/richtext-editor\";\n\nexport const Tone = Extension.create({\n  name: \"tone\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"paragraph\", \"heading\"],\n        attributes: {\n          tone: {\n            default: null,\n          },\n        },\n      },\n    ];\n  },\n\n  addHaloEditorMetadata() {\n    return {\n      contributions: [\n        {\n          targets: [\n            { kind: \"node\", name: \"paragraph\" },\n            { kind: \"node\", name: \"heading\" },\n          ],\n          metadata: {\n            ai: {\n              attributeGuidance: {\n                tone: {\n                  description: \"Writing tone for this block.\",\n                  allowedValues: [\"neutral\", \"friendly\", \"formal\"],\n                },\n              },\n            },\n          },\n        },\n      ],\n    };\n  },\n});\n```\n\n贡献只应用于最终 schema 中存在的目标。多个贡献冲突时，根据组件定义中的 priority 决定，\n较高者优先；priority 相同时，后注册者优先。\n\n## 组件自身的结构说明\n\n组件只声明自己的父级和数量关系，不跨组件声明子节点：\n\n```ts\naddHaloEditorMetadata() {\n  return {\n    ai: {\n      description: \"An optional caption belonging to a figure.\",\n    },\n    structure: {\n      allowedParents: [\"figure\"],\n      minPerParent: 0,\n      maxPerParent: 1,\n    },\n  };\n}\n```\n\n这表示当前组件只能位于 `figure` 下，在每个 `figure` 中可省略且最多出现一次。\n\n## 禁用与兼容\n\n- `ai: false` 明确建议 AI 不主动使用该组件，但组件的 schema 信息仍会出现在\n  Manifest 中供 AI 理解。\n- 声明抛错、字段无效、属性或父节点不存在时，生成器保留其他有效数据；\n  开发环境给出警告，生产环境不会因元数据阻止编辑器运行。\n- 未知字段会被丢弃。请勿把 system prompt、可执行回调或敏感信息放入元数据。\n\n每段文本最多 1,000 个字符；说明数组和 aliases 最多 10 项；\n`allowedValues` 和属性示例最多 32 项；组件 HTML 示例最多 3 个且每个不超过\n4 KiB；单组件 AI 元数据最多 16 KiB；单个 Manifest 的 AI 元数据最多\n128 KiB。\n\n## 读取运行期 Manifest\n\n在 Editor 创建完成后同步生成最终快照：\n\n```ts\nimport {\n  createHaloEditorManifest,\n  type HaloEditorManifest,\n  type VueEditor,\n} from \"@halo-dev/richtext-editor\";\n\nfunction editorManifest(editor: VueEditor): HaloEditorManifest {\n  return createHaloEditorManifest(editor);\n}\n```\n\nManifest 是当前 Editor 实例的运行期快照，包含全部 Node 和 Mark、规范化元数据、\n`version: 1` 以及稳定的 `signature`。AI 插件可以把它加入模型上下文，其他消费者\n也可以用它了解当前编辑器实际注册的组件；消费者自行决定是否根据其中的建议进行额外校验。\n\n<!-- File: .claude/commands/opsx/apply.md (Tokens: ~1238 | Category: command-prompt) -->\n---\nname: \"OPSX: Apply\"\ndescription: Implement tasks from an OpenSpec change (Experimental)\nallowed-tools: Bash(openspec:*)\ncategory: Workflow\ntags: [workflow, artifacts, experimental]\n---\n\nImplement tasks from an OpenSpec change.\n\n**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.\n\n**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.\n\n**Steps**\n\n1. **Select the change**\n\n   If a name is provided, use it. Otherwise:\n   - Infer from conversation context if the user mentioned a change\n   - Auto-select if only one active change exists\n   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select\n\n   Always announce: \"Using change: <name>\" and how to override (e.g., `/opsx:apply <other>`).\n\n2. **Check status to understand the schema**\n   ```bash\n   openspec status --change \"<name>\" --json\n   ```\n   Parse the JSON to understand:\n   - `schemaName`: The workflow being used (e.g., \"spec-driven\")\n   - `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints\n   - Which artifact contains the tasks (typically \"tasks\" for spec-driven, check status for others)\n\n3. **Get apply instructions**\n\n   ```bash\n   openspec instructions apply --change \"<name>\" --json\n   ```\n\n   This returns:\n   - `contextFiles`: artifact ID -> array of concrete file paths (varies by schema)\n   - Progress (total, complete, remaining)\n   - Task list with status\n   - Dynamic instruction based on current state\n\n   **Handle states:**\n   - If `state: \"blocked\"` (missing artifacts): show message, suggest using `/opsx:continue`\n   - If `state: \"all_done\"`: congratulate, suggest archive\n   - Otherwise: proceed to implementation\n\n4. **Read context files**\n\n   Read every file path listed under `contextFiles` from the apply instructions output.\n   The files depend on the schema being used:\n   - **spec-driven**: proposal, specs, design, tasks\n   - Other schemas: follow the contextFiles from CLI output\n\n5. **Show current progress**\n\n   Display:\n   - Schema being used\n   - Progress: \"N/M tasks complete\"\n   - Remaining tasks overview\n   - Dynamic instruction from CLI\n\n6. **Implement tasks (loop until done or blocked)**\n\n   For each pending task:\n   - Show which task is being worked on\n   - Make the code changes required\n   - Keep changes minimal and focused\n   - Mark task complete in the tasks file: `- [ ]` → `- [x]`\n   - Continue to next task\n\n   **Pause if:**\n   - Task is unclear → ask for clarification\n   - Implementation reveals a design issue → suggest updating artifacts\n   - Error or blocker encountered → report and wait for guidance\n   - User interrupts\n\n7. **On completion or pause, show status**\n\n   Display:\n   - Tasks completed this session\n   - Overall progress: \"N/M tasks complete\"\n   - If all done: suggest archive\n   - If paused: explain why and wait for guidance\n\n**Output During Implementation**\n\n```\n## Implementing: <change-name> (schema: <schema-name>)\n\nWorking on task 3/7: <task description>\n[...implementation happening...]\n✓ Task complete\n\nWorking on task 4/7: <task description>\n[...implementation happening...]\n✓ Task complete\n```\n\n**Output On Completion**\n\n```\n## Implementation Complete\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 7/7 tasks complete ✓\n\n### Completed This Session\n- [x] Task 1\n- [x] Task 2\n...\n\nAll tasks complete! You can archive this change with `/opsx:archive`.\n```\n\n**Output On Pause (Issue Encountered)**\n\n```\n## Implementation Paused\n\n**Change:** <change-name>\n**Schema:** <schema-name>\n**Progress:** 4/7 tasks complete\n\n### Issue Encountered\n<description of the issue>\n\n**Options:**\n1. <option 1>\n2. <option 2>\n3. Other approach\n\nWhat would you like to do?\n```\n\n**Guardrails**\n- Keep going through tasks until done or blocked\n- Always read context files before starting (from the apply instructions output)\n- If task is ambiguous, pause and ask before implementing\n- If implementation reveals issues, pause and suggest artifact updates\n- Keep code changes minimal and scoped to each task\n- Update task checkbox immediately after completing each task\n- Pause on errors, blockers, or unclear requirements - don't guess\n- Use contextFiles from CLI output, don't assume specific file names\n\n**Fluid Workflow Integration**\n\nThis skill supports the \"actions on a change\" model:\n\n- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions\n- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly\n\n<!-- ... +9 more modular skills available via /api/skills/halo-dev/halo?path={path} -->\n</yakaai_skills>"}