From ea9b35fdba15d06d69f0b9a5dd27799f892d9c6a Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 27 Dec 2025 11:38:15 -0600 Subject: [PATCH 01/15] MAESTRO: feat(openspec): create OpenSpec prompts directory with initial prompts Created src/prompts/openspec/ with all required prompt files mirroring the spec-kit pattern: - metadata.json: Initial metadata pointing to Fission-AI/OpenSpec repo - openspec.help.md: Maestro-specific help with spec-kit comparison - openspec.proposal.md: Stage 1 - Creating Changes workflow - openspec.apply.md: Stage 2 - Implementing Changes workflow - openspec.archive.md: Stage 3 - Archiving Changes workflow - openspec.implement.md: Maestro Auto Run integration with worktree support Each prompt was extracted/adapted from the OpenSpec AGENTS.md document and enhanced for Maestro's Auto Run integration. --- src/prompts/openspec/metadata.json | 6 + src/prompts/openspec/openspec.apply.md | 136 +++++++++++++ src/prompts/openspec/openspec.archive.md | 162 +++++++++++++++ src/prompts/openspec/openspec.help.md | 147 ++++++++++++++ src/prompts/openspec/openspec.implement.md | 224 +++++++++++++++++++++ src/prompts/openspec/openspec.proposal.md | 203 +++++++++++++++++++ 6 files changed, 878 insertions(+) create mode 100644 src/prompts/openspec/metadata.json create mode 100644 src/prompts/openspec/openspec.apply.md create mode 100644 src/prompts/openspec/openspec.archive.md create mode 100644 src/prompts/openspec/openspec.help.md create mode 100644 src/prompts/openspec/openspec.implement.md create mode 100644 src/prompts/openspec/openspec.proposal.md diff --git a/src/prompts/openspec/metadata.json b/src/prompts/openspec/metadata.json new file mode 100644 index 00000000..fb37cd42 --- /dev/null +++ b/src/prompts/openspec/metadata.json @@ -0,0 +1,6 @@ +{ + "lastRefreshed": "2025-01-01T00:00:00Z", + "commitSha": "main", + "sourceVersion": "0.1.0", + "sourceUrl": "https://github.com/Fission-AI/OpenSpec" +} diff --git a/src/prompts/openspec/openspec.apply.md b/src/prompts/openspec/openspec.apply.md new file mode 100644 index 00000000..011b3063 --- /dev/null +++ b/src/prompts/openspec/openspec.apply.md @@ -0,0 +1,136 @@ +# OpenSpec Apply - Stage 2: Implementing Changes + +You are helping the user implement an approved OpenSpec change proposal. This is Stage 2 of the OpenSpec workflow. + +## User Input + +```text +$ARGUMENTS +``` + +You **MUST** consider the user input before proceeding (if not empty). + +## Prerequisites + +**IMPORTANT: Do not start implementation until the proposal is reviewed and approved.** + +The approval gate ensures alignment between stakeholders before coding begins. If the proposal hasn't been approved yet, remind the user to get approval first. + +## Implementation Workflow + +### Step 1: Review the Proposal + +Read `proposal.md` to understand: +- **Why** - The problem or opportunity being addressed +- **What** - The specific changes being made +- **Impact** - Which specs, code, and systems are affected + +```bash +openspec show +``` + +### Step 2: Review Technical Decisions + +If `design.md` exists, review it for: +- Key architectural decisions +- Chosen approaches and trade-offs +- Migration plan (if applicable) +- Known risks to watch for + +### Step 3: Study the Tasks + +Read `tasks.md` for the implementation checklist: +- Tasks are numbered and ordered by dependency +- Each phase should be completed before moving to the next +- Tasks with `[P]` marker can run in parallel + +### Step 4: Execute Tasks Sequentially + +For each task in `tasks.md`: + +1. **Read the task description** carefully +2. **Implement the required changes** +3. **Verify the implementation** works as expected +4. **Test related functionality** to catch regressions +5. **Mark the task complete** by changing `- [ ]` to `- [x]` + +```markdown +# Before +- [ ] T001 Implement user authentication endpoint + +# After +- [x] T001 Implement user authentication endpoint +``` + +### Step 5: Confirm Completion + +Before marking the change as complete: + +1. **Verify every task** is checked (`- [x]`) +2. **Run validation** to ensure specs are consistent: + ```bash + openspec validate --strict + ``` +3. **Run project tests** to verify no regressions +4. **Review the spec deltas** match what was implemented + +## Implementation Guidelines + +### Following Task Order + +Tasks are dependency-ordered. Complete them in sequence unless marked with `[P]`: + +```markdown +- [x] T001 Setup database migration # Do first +- [x] T002 Create User model # Do second +- [x] T003 [P] Add unit tests # Can run parallel with T004 +- [x] T004 [P] Add integration tests # Can run parallel with T003 +- [x] T005 Update API documentation # Do after T003 & T004 +``` + +### Handling Blockers + +If you encounter a blocker: + +1. **Document the issue** in the task notes +2. **Ask clarifying questions** if requirements are unclear +3. **Update the proposal** if scope needs adjustment (requires re-approval) +4. **Don't skip tasks** - blockers should be resolved, not bypassed + +### Quality Checks + +For each task: + +- **Code quality** - Follow project conventions from `openspec/project.md` +- **Test coverage** - Add tests for new functionality +- **Documentation** - Update docs if behavior changes +- **Type safety** - Ensure type definitions are complete + +## Helpful Commands + +| Command | Purpose | +|---------|---------| +| `openspec show ` | Display full change details | +| `openspec validate --strict` | Validate change consistency | +| `openspec list` | View all active changes | +| `openspec diff ` | Show spec differences | + +## When Implementation is Complete + +Once all tasks are marked `[x]` and validation passes: + +1. **Commit your changes** with a descriptive message referencing the change-id +2. **Create a pull request** for review +3. **Wait for deployment** before proceeding to Stage 3 (Archive) + +```bash +git add . +git commit -m "feat: implement - " +``` + +## Key Principles + +- **Complete tasks in order** - Dependencies exist for a reason +- **Verify before marking done** - Each `[x]` represents tested, working code +- **Don't shortcut** - If something is unclear, ask rather than assume +- **Keep specs in sync** - Implemented behavior should match spec deltas diff --git a/src/prompts/openspec/openspec.archive.md b/src/prompts/openspec/openspec.archive.md new file mode 100644 index 00000000..5de3eb24 --- /dev/null +++ b/src/prompts/openspec/openspec.archive.md @@ -0,0 +1,162 @@ +# OpenSpec Archive - Stage 3: Archiving Changes + +You are helping the user archive a completed OpenSpec change. This is Stage 3 of the OpenSpec workflow. + +## User Input + +```text +$ARGUMENTS +``` + +You **MUST** consider the user input before proceeding (if not empty). + +## Prerequisites + +**IMPORTANT: Archive only after deployment.** + +Stage 3 should be run after: +1. All tasks in `tasks.md` are marked complete (`- [x]`) +2. The code has been reviewed and merged +3. The changes have been deployed to production (or the target environment) + +## Archive Workflow + +### Step 1: Verify Completion + +Before archiving, confirm: + +```bash +# Check all tasks are complete +openspec show + +# Validate the change passes all checks +openspec validate --strict +``` + +### Step 2: Archive the Change + +Move the change directory to archive with a date prefix: + +```bash +openspec archive --yes +``` + +This command: +1. **Moves** `openspec/changes//` to `openspec/changes/archive/YYYY-MM-DD-/` +2. **Updates** `openspec/specs/` with any capability changes +3. **Validates** the archived change passes all checks + +### Step 3: Verify Archive Success + +After archiving: + +```bash +# Confirm change is no longer in active list +openspec list + +# Verify specs were updated correctly +openspec validate --strict +``` + +## Manual Archive Process + +If you need to archive manually (without the CLI): + +### 1. Create Archive Directory + +```bash +mkdir -p openspec/changes/archive/$(date +%Y-%m-%d)- +mv openspec/changes//* openspec/changes/archive/$(date +%Y-%m-%d)-/ +rmdir openspec/changes/ +``` + +### 2. Update Main Specs + +If the change modified existing specs, merge the deltas: + +1. **For ADDED requirements** - Copy to `openspec/specs//spec.md` +2. **For MODIFIED requirements** - Replace the original requirement with the modified version +3. **For REMOVED requirements** - Delete from `openspec/specs//spec.md` +4. **For RENAMED requirements** - Update the name in place + +### 3. Clean Up Spec Deltas + +The archived change should retain its spec deltas for historical reference, but the main `specs/` directory should now reflect the deployed state. + +## Archive Options + +```bash +# Standard archive (prompts for confirmation) +openspec archive + +# Skip confirmation prompt +openspec archive --yes +openspec archive -y + +# Skip spec updates (for tooling-only changes) +openspec archive --skip-specs +``` + +**Note:** Always pass the change-id explicitly. Don't rely on positional inference. + +## Validation After Archive + +Run comprehensive validation to confirm: + +```bash +openspec validate --strict +``` + +This validates: +- All archived changes pass validation +- Main specs are internally consistent +- No orphaned references exist + +## Common Issues + +### Spec Conflicts + +If archiving fails due to spec conflicts: + +1. **Review the conflict** - Check which requirements clash +2. **Resolve manually** - Edit `specs//spec.md` to resolve +3. **Re-run validation** - Ensure consistency after manual fixes +4. **Document resolution** - Note any manual interventions + +### Missing Archive Directory + +If the archive directory doesn't exist: + +```bash +mkdir -p openspec/changes/archive +``` + +### Partial Archive + +If archive was interrupted: + +1. **Check for duplicate files** in both `changes/` and `changes/archive/` +2. **Complete the move** manually if needed +3. **Run validation** to confirm state is correct + +## Post-Archive Checklist + +After successfully archiving: + +- [ ] Change is in `openspec/changes/archive/YYYY-MM-DD-/` +- [ ] Active change list (`openspec list`) no longer shows it +- [ ] Main specs reflect deployed state +- [ ] Validation passes (`openspec validate --strict`) +- [ ] Git commit includes the archive change + +```bash +git add . +git commit -m "chore: archive after deployment" +``` + +## Key Principles + +- **Archive promptly** - Don't let changes linger after deployment +- **Keep history** - Archived changes serve as documentation +- **Specs are truth** - After archiving, `specs/` represents deployed reality +- **Validate always** - Run validation before and after archiving diff --git a/src/prompts/openspec/openspec.help.md b/src/prompts/openspec/openspec.help.md new file mode 100644 index 00000000..3b94f286 --- /dev/null +++ b/src/prompts/openspec/openspec.help.md @@ -0,0 +1,147 @@ +# OpenSpec Help + +You are explaining how to use **OpenSpec** within Maestro. OpenSpec is a spec-driven development tool from [Fission-AI/OpenSpec](https://github.com/Fission-AI/OpenSpec) that provides a structured workflow for managing code changes through specifications. + +## What is OpenSpec? + +OpenSpec implements a three-stage workflow for managing code changes: + +1. **Proposal** - Draft change specifications before coding begins +2. **Apply** - Implement tasks referencing agreed specs +3. **Archive** - Move completed work to archive after deployment + +Unlike spec-kit (which focuses on creating feature specifications), OpenSpec specializes in **change management** - tracking modifications to an existing system through detailed spec deltas. + +## Key Differences from Spec-Kit + +| Aspect | Spec-Kit | OpenSpec | +|--------|----------|----------| +| Focus | Feature specification | Change management | +| Output | Feature specs, plans, tasks | Proposals, spec deltas | +| Workflow | Constitution → Specify → Plan | Proposal → Apply → Archive | +| Artifact | `specs/[feature]/` | `openspec/changes/[id]/` | +| When to use | New features | Modifying existing features | + +**Use spec-kit for**: New features, greenfield development, establishing project foundations + +**Use OpenSpec for**: Feature modifications, breaking changes, refactoring, migrations + +## Directory Structure + +OpenSpec uses this directory layout: + +``` +openspec/ +├── project.md # Project conventions and context +├── specs/ # Deployed specifications (truth) +│ └── [capability]/ +│ └── spec.md +└── changes/ # Proposed modifications (in progress) + ├── [change-id]/ + │ ├── proposal.md # What and why + │ ├── tasks.md # Implementation checklist + │ ├── design.md # Optional technical decisions + │ └── specs/ # Spec deltas + │ └── [capability]/ + │ └── spec.md + └── archive/ # Completed changes + └── YYYY-MM-DD-[change-id]/ +``` + +## Core Commands + +### `/openspec.proposal` - Create Change Proposal +Start here when modifying existing functionality. This command helps you: +- Review existing specs and active changes +- Choose a unique change-id (kebab-case, verb-led like `add-`, `update-`, `remove-`) +- Scaffold `proposal.md`, `tasks.md`, and spec deltas +- Validate your proposal before sharing + +### `/openspec.apply` - Implement Changes +Use after your proposal is approved. This command guides you through: +- Reading the proposal and design documents +- Following the tasks checklist sequentially +- Marking tasks complete as you work +- Ensuring all items are finished before deployment + +### `/openspec.archive` - Archive Completed Changes +Use after deployment to finalize the change: +- Move change directory to archive with date prefix +- Update main specs if capabilities changed +- Validate the archived change passes all checks + +### `/openspec.implement` - Execute with Maestro Auto Run +**Maestro-specific command.** Converts your OpenSpec tasks into Auto Run documents: +- Read proposal and tasks from a specified change +- Convert to Auto Run document format with checkboxes +- Support worktree mode for parallel execution +- Group related tasks into phases + +## Spec Delta Format + +When modifying existing specs, OpenSpec uses operation headers: + +```markdown +## ADDED Requirements +New standalone capabilities + +## MODIFIED Requirements +Changed behavior of existing requirements + +## REMOVED Requirements +Deprecated features (include Reason and Migration) + +## RENAMED Requirements +Name-only changes (no behavior change) +``` + +Each requirement needs at least one scenario: + +```markdown +#### Scenario: User login success +- **WHEN** valid credentials provided +- **THEN** return JWT token +``` + +## Validation Commands + +Always validate before sharing your proposal: + +```bash +openspec validate --strict # Comprehensive validation +openspec list # View active changes +openspec list --specs # List existing specs +openspec show # Display change details +``` + +## Integration with Maestro Auto Run + +OpenSpec works seamlessly with Maestro's Auto Run feature: + +1. **Create proposal** with `/openspec.proposal` +2. **Get approval** from stakeholders +3. **Use `/openspec.implement`** to generate Auto Run documents +4. Documents are saved to `Auto Run Docs/` in your project +5. Each task becomes a checkbox item that Auto Run executes +6. Complete tasks are marked with implementation notes +7. **Archive** with `/openspec.archive` after deployment + +## Tips for Best Results + +- **Always review `project.md`** - Understand project conventions first +- **Check existing changes** - Run `openspec list` to avoid conflicts +- **Use verb-led IDs** - `add-auth`, `update-api`, `remove-legacy` +- **Include scenarios** - Every requirement needs at least one test scenario +- **Validate early** - Run validation before sharing proposals +- **Respect the approval gate** - Don't implement until proposal is approved +- **Archive promptly** - Clean up after deployment to keep changes directory focused + +## Learn More + +- [OpenSpec Repository](https://github.com/Fission-AI/OpenSpec) - Official documentation +- OpenSpec prompts update automatically when you click "Check for Updates" in Maestro settings +- Custom modifications to prompts are preserved across updates + +--- + +*This help command is a Maestro-specific addition to the OpenSpec workflow.* diff --git a/src/prompts/openspec/openspec.implement.md b/src/prompts/openspec/openspec.implement.md new file mode 100644 index 00000000..d183a631 --- /dev/null +++ b/src/prompts/openspec/openspec.implement.md @@ -0,0 +1,224 @@ +--- +description: Execute OpenSpec tasks using Maestro's Auto Run feature with optional git worktree support for parallel implementation. +--- + +## User Input + +```text +$ARGUMENTS +``` + +You **MUST** consider the user input before proceeding (if not empty). + +## Overview + +This command bridges OpenSpec with Maestro's Auto Run feature. It converts your OpenSpec tasks into Auto Run documents that Maestro can execute autonomously, enabling automated implementation with parallel execution support. + +## Implementation Workflow + +### Step 1: Locate Your OpenSpec Change + +Your OpenSpec change is located at: +``` +openspec/changes// +├── proposal.md # What and why +├── tasks.md # Implementation checklist +├── design.md # Optional: technical decisions +└── specs/ # Spec deltas +``` + +Verify the change exists and tasks are ready: +```bash +openspec show +``` + +### Step 2: Review Before Converting + +Before converting to Auto Run: + +1. **Confirm proposal is approved** - Don't automate unapproved changes +2. **Review tasks.md** - Understand what will be executed +3. **Check for dependencies** - Note which tasks must run sequentially + +### Step 3: Convert to Auto Run Document + +Create an Auto Run document from your OpenSpec tasks: + +1. **Read `tasks.md`** from the change directory +2. **Group tasks by phase** (Setup, Core, Testing, etc.) +3. **Convert to Auto Run checkbox format** +4. **Save to `Auto Run Docs/`** with descriptive filename + +**Auto Run Document Format:** + +```markdown +# [Change Title] - Implementation Tasks + +## Context + +This document implements OpenSpec change: `` + +**Proposal:** openspec/changes//proposal.md +**Design:** openspec/changes//design.md (if exists) + +## Phase 1: Setup + +- [ ] T001 Initial configuration +- [ ] T002 Database migrations + +## Phase 2: Core Implementation + +- [ ] T003 Main feature implementation +- [ ] T004 Supporting functionality + +## Phase 3: Testing & Documentation + +- [ ] T005 Add unit tests +- [ ] T006 Update documentation + +## Completion Checklist + +After all tasks complete: +- [ ] Run `openspec validate --strict` +- [ ] Review changes and create PR +- [ ] After deployment, run `/openspec.archive` +``` + +### Step 4: Configure Auto Run + +1. **Open the Right Bar** in Maestro (`Cmd/Ctrl + B`) +2. **Select the "Auto Run" tab** +3. **Set the Auto Run folder** to `Auto Run Docs/` +4. **Select your generated document** from the list + +### Step 5: Start Automated Implementation + +Auto Run will: +- Read each task from the document +- Execute tasks sequentially (respecting phase order) +- Mark tasks as completed (`[x]`) with implementation notes +- Handle parallel tasks when marked with `[P]` + +**To start:** Click "Run" or press `Cmd/Ctrl + Enter` in the Auto Run panel. + +## Advanced: Parallel Implementation with Git Worktrees + +For larger changes with independent components, use git worktrees to implement multiple phases in parallel. + +### What are Worktrees? + +Git worktrees let you have multiple working directories for the same repository: +- Multiple AI agents working on different branches simultaneously +- Isolated changes that won't conflict during development +- Easy merging when components are complete + +### Setting Up Parallel Implementation + +1. **Identify Independent Phases**: Look for phases that don't depend on each other + +2. **Enable Worktree Mode in Auto Run**: + - Toggle the worktree option in the Auto Run panel + - Maestro creates an isolated working directory for each session + - Each session gets its own feature branch + +3. **Assign Phases to Sessions**: + ``` + Session 1: Phase 1 (Setup) + Phase 2a (Core API) + Session 2: Phase 2b (Core UI) - if independent from API + Session 3: Phase 3 (Testing) - after Phases 2a and 2b merge + ``` + +4. **Merge When Complete**: + - Each session commits to its feature branch + - Use Maestro's git integration to merge branches + - Resolve any conflicts before final merge + +### Worktree Commands + +Maestro handles worktrees automatically, but for reference: + +```bash +# Create a worktree for a feature branch +git worktree add ../openspec--worktree + +# List existing worktrees +git worktree list + +# Remove a worktree when done +git worktree remove ../openspec--worktree +``` + +## Task Markers + +Auto Run understands these markers from OpenSpec: + +| Marker | Meaning | +|--------|---------| +| `- [ ]` | Incomplete task | +| `- [x]` | Completed task | +| `[P]` | Parallelizable (can run with other `[P]` tasks) | +| `T001` | Task identifier (preserves OpenSpec numbering) | + +**Example:** +```markdown +- [ ] T001 Setup project structure +- [ ] T002 [P] Configure database connection +- [ ] T003 [P] Configure cache layer +- [ ] T004 Integrate database and cache +``` + +Tasks T002 and T003 can run in parallel, but T004 waits for both to complete. + +## Best Practices + +1. **Complete Setup Phase First**: Always complete Phase 1 before parallelizing later phases + +2. **Respect Dependencies**: Tasks without `[P]` should run sequentially + +3. **Validate After Each Phase**: Run `openspec validate` to catch issues early + +4. **Keep Tasks Atomic**: Each task should be independently testable + +5. **Reference Specs**: Include links to relevant spec files for context + +6. **Document Decisions**: Add notes when implementation deviates from original plan + +## Integration with OpenSpec Workflow + +After Auto Run completes all tasks: + +1. **Run validation:** + ```bash + openspec validate --strict + ``` + +2. **Create pull request** with all changes + +3. **After deployment**, archive the change: + ```bash + openspec archive --yes + ``` + +## Troubleshooting + +### Tasks Not Executing + +- Check Auto Run is pointed to the correct document +- Verify tasks use proper checkbox format `- [ ]` +- Ensure no syntax errors in the markdown + +### Worktree Conflicts + +- Merge main branch into worktree before starting +- Keep worktree branches short-lived +- Clean up worktrees promptly after merging + +### Validation Failures + +- Review spec deltas match implementation +- Ensure all scenarios are satisfied +- Check for missing requirements + +--- + +*This implement command is a Maestro-specific addition to the OpenSpec workflow.* diff --git a/src/prompts/openspec/openspec.proposal.md b/src/prompts/openspec/openspec.proposal.md new file mode 100644 index 00000000..5726fcae --- /dev/null +++ b/src/prompts/openspec/openspec.proposal.md @@ -0,0 +1,203 @@ +# OpenSpec Proposal - Stage 1: Creating Changes + +You are helping the user create an OpenSpec change proposal. This is Stage 1 of the OpenSpec workflow. + +## User Input + +```text +$ARGUMENTS +``` + +You **MUST** consider the user input before proceeding (if not empty). + +## When to Create a Proposal + +Create a proposal when making substantial modifications: + +- Adding new features or functionality +- Implementing breaking changes (APIs, schemas) +- Changing architectural patterns +- Optimizing performance that affects behavior +- Updating security patterns + +**Skip proposal for:** bug fixes, typos, comments, non-breaking dependency updates, and configuration-only changes. + +## Stage 1 Workflow + +Follow these steps to create a complete change proposal: + +### Step 1: Review Existing Context + +Before creating a proposal: + +1. **Check `openspec/project.md`** for project conventions and context +2. **Run `openspec list`** to see active changes (avoid conflicts) +3. **Run `openspec list --specs`** to view existing capabilities +4. **Use `openspec show [spec]`** to review current specification state + +### Step 2: Choose a Unique Change-ID + +- Use **kebab-case** naming +- Use **verb-led** format: + - `add-` for new features + - `update-` for modifications + - `remove-` for deprecations + - `refactor-` for structural changes +- Ensure uniqueness (append `-2` or `-3` if needed) + +**Examples:** `add-user-authentication`, `update-api-v2-endpoints`, `remove-legacy-cache` + +### Step 3: Scaffold Directory Structure + +Create the following structure: + +``` +openspec/changes/[change-id]/ +├── proposal.md # What and why +├── tasks.md # Implementation checklist +├── design.md # Optional: technical decisions +└── specs/ # Spec deltas + └── [capability]/ + └── spec.md +``` + +### Step 4: Write proposal.md + +Create `proposal.md` with these sections: + +```markdown +# [Change Title] + +## Why + +1–2 sentences explaining the problem or opportunity this addresses. + +## What Changes + +- Bulleted list of modifications +- Mark breaking changes as **BREAKING** +- Be specific about affected components + +## Impact + +- List affected specs +- List affected code modules +- List affected external systems +``` + +### Step 5: Create Spec Deltas + +In `specs/[capability]/spec.md`, use operation headers: + +```markdown +## ADDED Requirements + +New, standalone capabilities that don't exist yet. + +### REQ-001: Requirement Title + +Description of the new requirement. + +#### Scenario: Success case +- **WHEN** specific conditions are met +- **THEN** expected outcome occurs + +## MODIFIED Requirements + +Changed behavior or scope of existing requirements. + +### REQ-002: Existing Requirement (Modified) + +Updated description reflecting new behavior. + +#### Scenario: Updated behavior +- **WHEN** new conditions apply +- **THEN** new outcome expected + +## REMOVED Requirements + +Deprecated features being removed. + +### REQ-003: Deprecated Feature + +**Reason:** Explain why this is being removed +**Migration:** Describe how users should migrate + +## RENAMED Requirements + +Name-only changes (no behavior change). + +### REQ-004: Old Name → New Name +``` + +**Critical Rules:** +- Include at least one `#### Scenario:` per requirement (use 4 hashtags) +- Use `**WHEN**` and `**THEN**` bullets within scenarios +- For MODIFIED requirements, copy the entire existing requirement block and edit it +- Header text must match exactly (whitespace-insensitive) when modifying + +### Step 6: Create tasks.md + +Create a structured implementation checklist: + +```markdown +# Implementation Tasks + +## Phase 1: Setup + +- [ ] T001 Initial setup task +- [ ] T002 Configuration task + +## Phase 2: Core Implementation + +- [ ] T003 Main feature implementation +- [ ] T004 Supporting functionality +- [ ] T005 Integration work + +## Phase 3: Testing & Documentation + +- [ ] T006 Add unit tests +- [ ] T007 Add integration tests +- [ ] T008 Update documentation +``` + +### Step 7: Optional - Create design.md + +Create `design.md` only when needed for: +- Cross-cutting changes affecting multiple systems +- New dependencies or external integrations +- Significant data model changes +- Security or performance complexity + +**design.md sections:** +- Context +- Goals / Non-Goals +- Key Decisions +- Risks & Trade-offs +- Migration Plan (if applicable) +- Open Questions + +### Step 8: Validate Before Sharing + +Always validate your proposal: + +```bash +openspec validate --strict +``` + +Fix all issues identified by validation. Use `openspec show --json --deltas-only` for debugging. + +## Key Principles + +- **Specs are truth** - The `specs/` directory reflects what is built +- **Changes are proposals** - The `changes/` directory contains planned modifications +- **Ask before ambiguity** - If the request is unclear, ask 1–2 clarifying questions before scaffolding + +## Helpful Commands + +| Command | Purpose | +|---------|---------| +| `openspec list` | View active changes | +| `openspec list --specs` | List existing capabilities | +| `openspec show [item]` | Display change or spec details | +| `openspec validate [item] --strict` | Comprehensive validation | From 6514d0d325d26df69f7c7a7ea1eddf6f8563c56a Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 27 Dec 2025 11:41:17 -0600 Subject: [PATCH 02/15] MAESTRO: feat(openspec): add index module for OpenSpec prompts Create src/prompts/openspec/index.ts following the speckit pattern: - Import all 5 prompts using Vite's ?raw suffix - Define OpenSpecCommandDefinition and OpenSpecMetadata interfaces - Export openspecCommands array with help, proposal, apply, archive, implement - Add helper functions: getOpenSpecCommand(), getOpenSpecCommandBySlash(), getOpenSpecMetadata() - Export individual prompts for direct access --- src/prompts/openspec/index.ts | 117 ++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/prompts/openspec/index.ts diff --git a/src/prompts/openspec/index.ts b/src/prompts/openspec/index.ts new file mode 100644 index 00000000..1ce7d123 --- /dev/null +++ b/src/prompts/openspec/index.ts @@ -0,0 +1,117 @@ +/** + * OpenSpec prompts module + * + * Bundled prompts for the OpenSpec workflow from Fission-AI with our custom Maestro integration. + * These prompts are imported at build time using Vite's ?raw suffix. + * + * OpenSpec provides a structured change management workflow: + * - Proposal → Draft change specifications before coding + * - Apply → Implement tasks referencing agreed specs + * - Archive → Move completed work to archive after deployment + * + * Source: https://github.com/Fission-AI/OpenSpec + * Version: 0.1.0 + */ + +// Bundled OpenSpec prompts (extracted from upstream AGENTS.md) +import proposalPrompt from './openspec.proposal.md?raw'; +import applyPrompt from './openspec.apply.md?raw'; +import archivePrompt from './openspec.archive.md?raw'; + +// Custom Maestro prompts +import helpPrompt from './openspec.help.md?raw'; +import implementPrompt from './openspec.implement.md?raw'; + +// Metadata +import metadataJson from './metadata.json'; + +export interface OpenSpecCommandDefinition { + id: string; + command: string; + description: string; + prompt: string; + isCustom: boolean; +} + +export interface OpenSpecMetadata { + lastRefreshed: string; + commitSha: string; + sourceVersion: string; + sourceUrl: string; +} + +/** + * All bundled OpenSpec commands + */ +export const openspecCommands: OpenSpecCommandDefinition[] = [ + { + id: 'help', + command: '/openspec.help', + description: 'Learn how to use OpenSpec with Maestro', + prompt: helpPrompt, + isCustom: true, + }, + { + id: 'proposal', + command: '/openspec.proposal', + description: 'Create a change proposal with specs, tasks, and optional design docs', + prompt: proposalPrompt, + isCustom: false, + }, + { + id: 'apply', + command: '/openspec.apply', + description: 'Implement an approved change proposal by executing tasks', + prompt: applyPrompt, + isCustom: false, + }, + { + id: 'archive', + command: '/openspec.archive', + description: 'Archive a completed change after deployment', + prompt: archivePrompt, + isCustom: false, + }, + { + id: 'implement', + command: '/openspec.implement', + description: 'Convert OpenSpec tasks to Maestro Auto Run documents', + prompt: implementPrompt, + isCustom: true, + }, +]; + +/** + * Get an OpenSpec command by ID + */ +export function getOpenSpecCommand(id: string): OpenSpecCommandDefinition | undefined { + return openspecCommands.find((cmd) => cmd.id === id); +} + +/** + * Get an OpenSpec command by slash command string + */ +export function getOpenSpecCommandBySlash(command: string): OpenSpecCommandDefinition | undefined { + return openspecCommands.find((cmd) => cmd.command === command); +} + +/** + * Get the metadata for bundled OpenSpec prompts + */ +export function getOpenSpecMetadata(): OpenSpecMetadata { + return { + lastRefreshed: metadataJson.lastRefreshed, + commitSha: metadataJson.commitSha, + sourceVersion: metadataJson.sourceVersion, + sourceUrl: metadataJson.sourceUrl, + }; +} + +// Export individual prompts for direct access +export { + helpPrompt, + proposalPrompt, + applyPrompt, + archivePrompt, + implementPrompt, +}; From f014ae79ebcad00e9d62ae9b0184a6fb3a1422ed Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 27 Dec 2025 11:44:21 -0600 Subject: [PATCH 03/15] MAESTRO: feat(openspec): create OpenSpec manager module Mirrors speckit-manager.ts with full parity for OpenSpec workflow: - OPENSPEC_COMMANDS constant with 5 commands (help, proposal, apply, archive, implement) - User customization storage with loadUserCustomizations/saveUserCustomizations - Path resolution for dev/production bundled prompts - getBundledPrompts/getBundledMetadata with user prompts fallback - AGENTS.md parser to extract proposal/apply/archive sections - GitHub refresh via refreshOpenSpecPrompts() fetching from Fission-AI/OpenSpec - Complete exported API: getOpenSpecMetadata, getOpenSpecPrompts, saveOpenSpecPrompt, resetOpenSpecPrompt, getOpenSpecCommand, getOpenSpecCommandBySlash --- src/main/openspec-manager.ts | 435 +++++++++++++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 src/main/openspec-manager.ts diff --git a/src/main/openspec-manager.ts b/src/main/openspec-manager.ts new file mode 100644 index 00000000..0fa6b839 --- /dev/null +++ b/src/main/openspec-manager.ts @@ -0,0 +1,435 @@ +/** + * OpenSpec Manager + * + * Manages bundled OpenSpec prompts with support for: + * - Loading bundled prompts from src/prompts/openspec/ + * - Fetching updates from GitHub's OpenSpec repository + * - User customization with ability to reset to defaults + * + * OpenSpec provides a structured change management workflow: + * - Proposal → Draft change specifications before coding + * - Apply → Implement tasks referencing agreed specs + * - Archive → Move completed work to archive after deployment + * + * Source: https://github.com/Fission-AI/OpenSpec + */ + +import fs from 'fs/promises'; +import path from 'path'; +import { app } from 'electron'; +import { logger } from './utils/logger'; + +const LOG_CONTEXT = '[OpenSpec]'; + +// All bundled OpenSpec commands with their metadata +const OPENSPEC_COMMANDS = [ + { id: 'help', command: '/openspec.help', description: 'Learn how to use OpenSpec with Maestro', isCustom: true }, + { id: 'proposal', command: '/openspec.proposal', description: 'Create a change proposal with specs, tasks, and optional design docs', isCustom: false }, + { id: 'apply', command: '/openspec.apply', description: 'Implement an approved change proposal by executing tasks', isCustom: false }, + { id: 'archive', command: '/openspec.archive', description: 'Archive a completed change after deployment', isCustom: false }, + { id: 'implement', command: '/openspec.implement', description: 'Convert OpenSpec tasks to Maestro Auto Run documents', isCustom: true }, +] as const; + +export interface OpenSpecCommand { + id: string; + command: string; + description: string; + prompt: string; + isCustom: boolean; + isModified: boolean; +} + +export interface OpenSpecMetadata { + lastRefreshed: string; + commitSha: string; + sourceVersion: string; + sourceUrl: string; +} + +interface StoredPrompt { + content: string; + isModified: boolean; + modifiedAt?: string; +} + +interface StoredData { + metadata: OpenSpecMetadata; + prompts: Record; +} + +/** + * Get path to user's OpenSpec customizations file + */ +function getUserDataPath(): string { + return path.join(app.getPath('userData'), 'openspec-customizations.json'); +} + +/** + * Load user customizations from disk + */ +async function loadUserCustomizations(): Promise { + try { + const content = await fs.readFile(getUserDataPath(), 'utf-8'); + return JSON.parse(content); + } catch { + return null; + } +} + +/** + * Save user customizations to disk + */ +async function saveUserCustomizations(data: StoredData): Promise { + await fs.writeFile(getUserDataPath(), JSON.stringify(data, null, 2), 'utf-8'); +} + +/** + * Get the path to bundled prompts directory + * In development, this is src/prompts/openspec + * In production, this is in the app resources + */ +function getBundledPromptsPath(): string { + if (app.isPackaged) { + return path.join(process.resourcesPath, 'prompts', 'openspec'); + } + // In development, use the source directory + return path.join(__dirname, '..', '..', 'src', 'prompts', 'openspec'); +} + +/** + * Get the user data directory for storing downloaded OpenSpec prompts + */ +function getUserPromptsPath(): string { + return path.join(app.getPath('userData'), 'openspec-prompts'); +} + +/** + * Get bundled prompts by reading from disk + * Checks user prompts directory first (for downloaded updates), then falls back to bundled + */ +async function getBundledPrompts(): Promise> { + const bundledPromptsDir = getBundledPromptsPath(); + const userPromptsDir = getUserPromptsPath(); + const result: Record = {}; + + for (const cmd of OPENSPEC_COMMANDS) { + // For custom commands, always use bundled + if (cmd.isCustom) { + try { + const promptPath = path.join(bundledPromptsDir, `openspec.${cmd.id}.md`); + const prompt = await fs.readFile(promptPath, 'utf-8'); + result[cmd.id] = { + prompt, + description: cmd.description, + isCustom: cmd.isCustom, + }; + } catch (error) { + logger.warn(`Failed to load bundled prompt for ${cmd.id}: ${error}`, LOG_CONTEXT); + result[cmd.id] = { + prompt: `# ${cmd.id}\n\nPrompt not available.`, + description: cmd.description, + isCustom: cmd.isCustom, + }; + } + continue; + } + + // For upstream commands, check user prompts directory first (downloaded updates) + try { + const userPromptPath = path.join(userPromptsDir, `openspec.${cmd.id}.md`); + const prompt = await fs.readFile(userPromptPath, 'utf-8'); + result[cmd.id] = { + prompt, + description: cmd.description, + isCustom: cmd.isCustom, + }; + continue; + } catch { + // User prompt not found, try bundled + } + + // Fall back to bundled prompts + try { + const promptPath = path.join(bundledPromptsDir, `openspec.${cmd.id}.md`); + const prompt = await fs.readFile(promptPath, 'utf-8'); + result[cmd.id] = { + prompt, + description: cmd.description, + isCustom: cmd.isCustom, + }; + } catch (error) { + logger.warn(`Failed to load bundled prompt for ${cmd.id}: ${error}`, LOG_CONTEXT); + result[cmd.id] = { + prompt: `# ${cmd.id}\n\nPrompt not available.`, + description: cmd.description, + isCustom: cmd.isCustom, + }; + } + } + + return result; +} + +/** + * Get bundled metadata by reading from disk + * Checks user prompts directory first (for downloaded updates), then falls back to bundled + */ +async function getBundledMetadata(): Promise { + const bundledPromptsDir = getBundledPromptsPath(); + const userPromptsDir = getUserPromptsPath(); + + // Check user prompts directory first (downloaded updates) + try { + const userMetadataPath = path.join(userPromptsDir, 'metadata.json'); + const content = await fs.readFile(userMetadataPath, 'utf-8'); + return JSON.parse(content); + } catch { + // User metadata not found, try bundled + } + + // Fall back to bundled metadata + try { + const metadataPath = path.join(bundledPromptsDir, 'metadata.json'); + const content = await fs.readFile(metadataPath, 'utf-8'); + return JSON.parse(content); + } catch { + // Return default metadata if file doesn't exist + return { + lastRefreshed: '2025-01-01T00:00:00Z', + commitSha: 'main', + sourceVersion: '0.1.0', + sourceUrl: 'https://github.com/Fission-AI/OpenSpec', + }; + } +} + +/** + * Get current OpenSpec metadata + */ +export async function getOpenSpecMetadata(): Promise { + const customizations = await loadUserCustomizations(); + if (customizations?.metadata) { + return customizations.metadata; + } + return getBundledMetadata(); +} + +/** + * Get all OpenSpec prompts (bundled defaults merged with user customizations) + */ +export async function getOpenSpecPrompts(): Promise { + const bundled = await getBundledPrompts(); + const customizations = await loadUserCustomizations(); + + const commands: OpenSpecCommand[] = []; + + for (const [id, data] of Object.entries(bundled)) { + const customPrompt = customizations?.prompts?.[id]; + const isModified = customPrompt?.isModified ?? false; + const prompt = isModified && customPrompt ? customPrompt.content : data.prompt; + + commands.push({ + id, + command: `/openspec.${id}`, + description: data.description, + prompt, + isCustom: data.isCustom, + isModified, + }); + } + + return commands; +} + +/** + * Save user's edit to an OpenSpec prompt + */ +export async function saveOpenSpecPrompt(id: string, content: string): Promise { + const customizations = await loadUserCustomizations() ?? { + metadata: await getBundledMetadata(), + prompts: {}, + }; + + customizations.prompts[id] = { + content, + isModified: true, + modifiedAt: new Date().toISOString(), + }; + + await saveUserCustomizations(customizations); + logger.info(`Saved customization for openspec.${id}`, LOG_CONTEXT); +} + +/** + * Reset an OpenSpec prompt to its bundled default + */ +export async function resetOpenSpecPrompt(id: string): Promise { + const bundled = await getBundledPrompts(); + const defaultPrompt = bundled[id]; + + if (!defaultPrompt) { + throw new Error(`Unknown openspec command: ${id}`); + } + + const customizations = await loadUserCustomizations(); + if (customizations?.prompts?.[id]) { + delete customizations.prompts[id]; + await saveUserCustomizations(customizations); + logger.info(`Reset openspec.${id} to bundled default`, LOG_CONTEXT); + } + + return defaultPrompt.prompt; +} + +/** + * Upstream commands to fetch (we skip custom commands like 'help' and 'implement') + */ +const UPSTREAM_COMMANDS = ['proposal', 'apply', 'archive']; + +/** + * Section markers in AGENTS.md for extracting workflow prompts + */ +const SECTION_MARKERS: Record = { + proposal: { + start: /^#+\s*Stage\s*1[:\s]+Creating\s+Changes/i, + end: /^#+\s*Stage\s*2[:\s]+/i, + }, + apply: { + start: /^#+\s*Stage\s*2[:\s]+Implementing\s+Changes/i, + end: /^#+\s*Stage\s*3[:\s]+/i, + }, + archive: { + start: /^#+\s*Stage\s*3[:\s]+Archiving\s+Changes/i, + end: /^$/, // End of file or next major section + }, +}; + +/** + * Parse AGENTS.md and extract workflow sections as prompts + */ +function parseAgentsMd(content: string): Record { + const result: Record = {}; + const lines = content.split('\n'); + + for (const [sectionId, markers] of Object.entries(SECTION_MARKERS)) { + let inSection = false; + let sectionLines: string[] = []; + + for (const line of lines) { + if (!inSection && markers.start.test(line)) { + inSection = true; + sectionLines.push(line); + continue; + } + + if (inSection) { + // Check if we've hit the end marker (next stage or end of content) + if (markers.end.test(line) && line.trim() !== '') { + // Don't include the end marker line, it belongs to the next section + break; + } + sectionLines.push(line); + } + } + + if (sectionLines.length > 0) { + result[sectionId] = sectionLines.join('\n').trim(); + } + } + + return result; +} + +/** + * Fetch latest prompts from GitHub OpenSpec repository + * Updates all upstream commands by parsing AGENTS.md + */ +export async function refreshOpenSpecPrompts(): Promise { + logger.info('Refreshing OpenSpec prompts from GitHub...', LOG_CONTEXT); + + // Fetch AGENTS.md from the repository + const agentsMdUrl = 'https://raw.githubusercontent.com/Fission-AI/OpenSpec/main/openspec/AGENTS.md'; + const agentsResponse = await fetch(agentsMdUrl); + if (!agentsResponse.ok) { + throw new Error(`Failed to fetch AGENTS.md: ${agentsResponse.statusText}`); + } + const agentsMdContent = await agentsResponse.text(); + logger.info('Downloaded AGENTS.md', LOG_CONTEXT); + + // Parse the AGENTS.md content to extract sections + const extractedPrompts = parseAgentsMd(agentsMdContent); + logger.info(`Extracted ${Object.keys(extractedPrompts).length} sections from AGENTS.md`, LOG_CONTEXT); + + // Get the latest commit SHA for version tracking + let commitSha = 'main'; + try { + const commitsResponse = await fetch('https://api.github.com/repos/Fission-AI/OpenSpec/commits/main', { + headers: { 'User-Agent': 'Maestro-OpenSpec-Refresher' }, + }); + if (commitsResponse.ok) { + const commitInfo = await commitsResponse.json() as { sha: string }; + commitSha = commitInfo.sha.substring(0, 7); + } + } catch { + logger.warn('Could not fetch commit SHA, using "main"', LOG_CONTEXT); + } + + // Create user prompts directory + const userPromptsDir = getUserPromptsPath(); + await fs.mkdir(userPromptsDir, { recursive: true }); + + // Save extracted prompts + for (const cmdId of UPSTREAM_COMMANDS) { + const promptContent = extractedPrompts[cmdId]; + if (promptContent) { + const destPath = path.join(userPromptsDir, `openspec.${cmdId}.md`); + await fs.writeFile(destPath, promptContent, 'utf8'); + logger.info(`Updated: openspec.${cmdId}.md`, LOG_CONTEXT); + } else { + logger.warn(`Could not extract ${cmdId} section from AGENTS.md`, LOG_CONTEXT); + } + } + + // Update metadata with new version info + const newMetadata: OpenSpecMetadata = { + lastRefreshed: new Date().toISOString(), + commitSha, + sourceVersion: '0.1.0', + sourceUrl: 'https://github.com/Fission-AI/OpenSpec', + }; + + // Save metadata to user prompts directory + await fs.writeFile( + path.join(userPromptsDir, 'metadata.json'), + JSON.stringify(newMetadata, null, 2), + 'utf8' + ); + + // Also save to customizations file for compatibility + const customizations = await loadUserCustomizations() ?? { + metadata: newMetadata, + prompts: {}, + }; + customizations.metadata = newMetadata; + await saveUserCustomizations(customizations); + + logger.info(`Refreshed OpenSpec prompts (commit: ${commitSha})`, LOG_CONTEXT); + + return newMetadata; +} + +/** + * Get a single OpenSpec command by ID + */ +export async function getOpenSpecCommand(id: string): Promise { + const commands = await getOpenSpecPrompts(); + return commands.find((cmd) => cmd.id === id) ?? null; +} + +/** + * Get an OpenSpec command by its slash command string (e.g., "/openspec.proposal") + */ +export async function getOpenSpecCommandBySlash(slashCommand: string): Promise { + const commands = await getOpenSpecPrompts(); + return commands.find((cmd) => cmd.command === slashCommand) ?? null; +} From 282d87b0fbddcc123b132b5aa709dfa630c8eedb Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 27 Dec 2025 11:48:01 -0600 Subject: [PATCH 04/15] MAESTRO: feat(openspec): add GitHub refresh script and npm command - Create scripts/refresh-openspec.mjs for build-time prompt updates - Add refresh-openspec npm script to package.json - Add extraResources for openspec prompts in all platform builds - Update bundled prompts with latest from OpenSpec repository --- package.json | 15 +- scripts/refresh-openspec.mjs | 216 ++++++++++++++++++++ src/prompts/openspec/metadata.json | 6 +- src/prompts/openspec/openspec.apply.md | 145 +------------- src/prompts/openspec/openspec.archive.md | 168 +--------------- src/prompts/openspec/openspec.proposal.md | 234 +++------------------- 6 files changed, 279 insertions(+), 505 deletions(-) create mode 100644 scripts/refresh-openspec.mjs diff --git a/package.json b/package.json index 89468c24..72a2c52d 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "test:integration": "vitest run --config vitest.integration.config.ts", "test:integration:watch": "vitest --config vitest.integration.config.ts", "test:performance": "vitest run --config vitest.performance.config.mts", - "refresh-speckit": "node scripts/refresh-speckit.mjs" + "refresh-speckit": "node scripts/refresh-speckit.mjs", + "refresh-openspec": "node scripts/refresh-openspec.mjs" }, "build": { "npmRebuild": false, @@ -99,6 +100,10 @@ { "from": "src/prompts/speckit", "to": "prompts/speckit" + }, + { + "from": "src/prompts/openspec", + "to": "prompts/openspec" } ] }, @@ -127,6 +132,10 @@ { "from": "src/prompts/speckit", "to": "prompts/speckit" + }, + { + "from": "src/prompts/openspec", + "to": "prompts/openspec" } ] }, @@ -164,6 +173,10 @@ { "from": "src/prompts/speckit", "to": "prompts/speckit" + }, + { + "from": "src/prompts/openspec", + "to": "prompts/openspec" } ] }, diff --git a/scripts/refresh-openspec.mjs b/scripts/refresh-openspec.mjs new file mode 100644 index 00000000..2a3c9089 --- /dev/null +++ b/scripts/refresh-openspec.mjs @@ -0,0 +1,216 @@ +#!/usr/bin/env node +/** + * Refresh OpenSpec Prompts + * + * Fetches the latest OpenSpec prompts from GitHub by parsing AGENTS.md + * and extracts the three workflow stages (proposal, apply, archive). + * + * Unlike spec-kit which uses ZIP releases, OpenSpec bundles all workflow + * instructions in a single AGENTS.md file that we parse into sections. + * + * Usage: npm run refresh-openspec + */ + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import https from 'https'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const OPENSPEC_DIR = path.join(__dirname, '..', 'src', 'prompts', 'openspec'); +const METADATA_PATH = path.join(OPENSPEC_DIR, 'metadata.json'); + +// GitHub OpenSpec repository info +const GITHUB_API = 'https://api.github.com'; +const REPO_OWNER = 'Fission-AI'; +const REPO_NAME = 'OpenSpec'; +const AGENTS_MD_URL = `https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/main/openspec/AGENTS.md`; + +// Commands to extract from AGENTS.md (we skip custom commands like 'help' and 'implement') +const UPSTREAM_COMMANDS = ['proposal', 'apply', 'archive']; + +// Section markers for parsing AGENTS.md +// Stage headers are formatted as: ### Stage N: Title +const SECTION_MARKERS = { + proposal: { + start: /^###\s*Stage\s*1[:\s]+Creating\s+Changes/i, + end: /^###\s*Stage\s*2[:\s]+/i, + }, + apply: { + start: /^###\s*Stage\s*2[:\s]+Implementing\s+Changes/i, + end: /^###\s*Stage\s*3[:\s]+/i, + }, + archive: { + start: /^###\s*Stage\s*3[:\s]+Archiving\s+Changes/i, + end: /^##[^#]/, // End at next level-2 heading or end of file + }, +}; + +/** + * Make an HTTPS GET request + */ +function httpsGet(url, options = {}) { + return new Promise((resolve, reject) => { + const headers = { + 'User-Agent': 'Maestro-OpenSpec-Refresher', + ...options.headers, + }; + + https.get(url, { headers }, (res) => { + // Handle redirects + if (res.statusCode === 301 || res.statusCode === 302) { + return resolve(httpsGet(res.headers.location, options)); + } + + if (res.statusCode !== 200) { + reject(new Error(`HTTP ${res.statusCode}: ${url}`)); + return; + } + + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => resolve({ data, headers: res.headers })); + res.on('error', reject); + }).on('error', reject); + }); +} + +/** + * Parse AGENTS.md and extract workflow sections as prompts + */ +function parseAgentsMd(content) { + const result = {}; + const lines = content.split('\n'); + + for (const [sectionId, markers] of Object.entries(SECTION_MARKERS)) { + let inSection = false; + let sectionLines = []; + + for (const line of lines) { + if (!inSection && markers.start.test(line)) { + inSection = true; + sectionLines.push(line); + continue; + } + + if (inSection) { + // Check if we've hit the end marker (next stage or next major section) + if (markers.end.test(line) && line.trim() !== '') { + // Don't include the end marker line, it belongs to the next section + break; + } + sectionLines.push(line); + } + } + + if (sectionLines.length > 0) { + // Clean up trailing empty lines + while (sectionLines.length > 0 && sectionLines[sectionLines.length - 1].trim() === '') { + sectionLines.pop(); + } + result[sectionId] = sectionLines.join('\n').trim(); + } + } + + return result; +} + +/** + * Get the latest commit SHA from the main branch + */ +async function getLatestCommitSha() { + try { + const url = `${GITHUB_API}/repos/${REPO_OWNER}/${REPO_NAME}/commits/main`; + const { data } = await httpsGet(url); + const commit = JSON.parse(data); + return commit.sha.substring(0, 7); + } catch (error) { + console.warn(' Warning: Could not fetch commit SHA, using "main"'); + return 'main'; + } +} + +/** + * Main refresh function + */ +async function refreshOpenSpec() { + console.log('🔄 Refreshing OpenSpec prompts from GitHub...\n'); + + // Ensure openspec directory exists + if (!fs.existsSync(OPENSPEC_DIR)) { + console.error('❌ OpenSpec directory not found:', OPENSPEC_DIR); + process.exit(1); + } + + try { + // Fetch AGENTS.md + console.log('📡 Fetching AGENTS.md from OpenSpec repository...'); + const { data: agentsMdContent } = await httpsGet(AGENTS_MD_URL); + console.log(` Downloaded AGENTS.md (${agentsMdContent.length} bytes)`); + + // Parse sections + console.log('\n📦 Parsing workflow sections...'); + const extractedPrompts = parseAgentsMd(agentsMdContent); + const extractedCount = Object.keys(extractedPrompts).length; + console.log(` Extracted ${extractedCount} sections from AGENTS.md`); + + if (extractedCount === 0) { + console.error('❌ Failed to extract any sections from AGENTS.md'); + console.error(' Check that the section markers match the current format'); + process.exit(1); + } + + // Get commit SHA for version tracking + console.log('\n📋 Getting version info...'); + const commitSha = await getLatestCommitSha(); + console.log(` Commit: ${commitSha}`); + + // Update prompt files + console.log('\n✏️ Updating prompt files...'); + let updatedCount = 0; + for (const commandName of UPSTREAM_COMMANDS) { + const content = extractedPrompts[commandName]; + if (!content) { + console.log(` ⚠ Missing: openspec.${commandName}.md (section not found)`); + continue; + } + + const promptFile = path.join(OPENSPEC_DIR, `openspec.${commandName}.md`); + const existingContent = fs.existsSync(promptFile) + ? fs.readFileSync(promptFile, 'utf8') + : ''; + + if (content !== existingContent) { + fs.writeFileSync(promptFile, content); + console.log(` ✓ Updated: openspec.${commandName}.md`); + updatedCount++; + } else { + console.log(` - Unchanged: openspec.${commandName}.md`); + } + } + + // Update metadata + const metadata = { + lastRefreshed: new Date().toISOString(), + commitSha, + sourceVersion: '0.1.0', + sourceUrl: `https://github.com/${REPO_OWNER}/${REPO_NAME}`, + }; + + fs.writeFileSync(METADATA_PATH, JSON.stringify(metadata, null, 2)); + console.log('\n📄 Updated metadata.json'); + + // Summary + console.log('\n✅ Refresh complete!'); + console.log(` Commit: ${commitSha}`); + console.log(` Updated: ${updatedCount} files`); + console.log(` Skipped: help, implement (custom Maestro prompts)`); + + } catch (error) { + console.error('\n❌ Refresh failed:', error.message); + process.exit(1); + } +} + +// Run +refreshOpenSpec(); diff --git a/src/prompts/openspec/metadata.json b/src/prompts/openspec/metadata.json index fb37cd42..765495e7 100644 --- a/src/prompts/openspec/metadata.json +++ b/src/prompts/openspec/metadata.json @@ -1,6 +1,6 @@ { - "lastRefreshed": "2025-01-01T00:00:00Z", - "commitSha": "main", + "lastRefreshed": "2025-12-27T17:46:57.133Z", + "commitSha": "8dcd170", "sourceVersion": "0.1.0", "sourceUrl": "https://github.com/Fission-AI/OpenSpec" -} +} \ No newline at end of file diff --git a/src/prompts/openspec/openspec.apply.md b/src/prompts/openspec/openspec.apply.md index 011b3063..0a43fc8b 100644 --- a/src/prompts/openspec/openspec.apply.md +++ b/src/prompts/openspec/openspec.apply.md @@ -1,136 +1,9 @@ -# OpenSpec Apply - Stage 2: Implementing Changes - -You are helping the user implement an approved OpenSpec change proposal. This is Stage 2 of the OpenSpec workflow. - -## User Input - -```text -$ARGUMENTS -``` - -You **MUST** consider the user input before proceeding (if not empty). - -## Prerequisites - -**IMPORTANT: Do not start implementation until the proposal is reviewed and approved.** - -The approval gate ensures alignment between stakeholders before coding begins. If the proposal hasn't been approved yet, remind the user to get approval first. - -## Implementation Workflow - -### Step 1: Review the Proposal - -Read `proposal.md` to understand: -- **Why** - The problem or opportunity being addressed -- **What** - The specific changes being made -- **Impact** - Which specs, code, and systems are affected - -```bash -openspec show -``` - -### Step 2: Review Technical Decisions - -If `design.md` exists, review it for: -- Key architectural decisions -- Chosen approaches and trade-offs -- Migration plan (if applicable) -- Known risks to watch for - -### Step 3: Study the Tasks - -Read `tasks.md` for the implementation checklist: -- Tasks are numbered and ordered by dependency -- Each phase should be completed before moving to the next -- Tasks with `[P]` marker can run in parallel - -### Step 4: Execute Tasks Sequentially - -For each task in `tasks.md`: - -1. **Read the task description** carefully -2. **Implement the required changes** -3. **Verify the implementation** works as expected -4. **Test related functionality** to catch regressions -5. **Mark the task complete** by changing `- [ ]` to `- [x]` - -```markdown -# Before -- [ ] T001 Implement user authentication endpoint - -# After -- [x] T001 Implement user authentication endpoint -``` - -### Step 5: Confirm Completion - -Before marking the change as complete: - -1. **Verify every task** is checked (`- [x]`) -2. **Run validation** to ensure specs are consistent: - ```bash - openspec validate --strict - ``` -3. **Run project tests** to verify no regressions -4. **Review the spec deltas** match what was implemented - -## Implementation Guidelines - -### Following Task Order - -Tasks are dependency-ordered. Complete them in sequence unless marked with `[P]`: - -```markdown -- [x] T001 Setup database migration # Do first -- [x] T002 Create User model # Do second -- [x] T003 [P] Add unit tests # Can run parallel with T004 -- [x] T004 [P] Add integration tests # Can run parallel with T003 -- [x] T005 Update API documentation # Do after T003 & T004 -``` - -### Handling Blockers - -If you encounter a blocker: - -1. **Document the issue** in the task notes -2. **Ask clarifying questions** if requirements are unclear -3. **Update the proposal** if scope needs adjustment (requires re-approval) -4. **Don't skip tasks** - blockers should be resolved, not bypassed - -### Quality Checks - -For each task: - -- **Code quality** - Follow project conventions from `openspec/project.md` -- **Test coverage** - Add tests for new functionality -- **Documentation** - Update docs if behavior changes -- **Type safety** - Ensure type definitions are complete - -## Helpful Commands - -| Command | Purpose | -|---------|---------| -| `openspec show ` | Display full change details | -| `openspec validate --strict` | Validate change consistency | -| `openspec list` | View all active changes | -| `openspec diff ` | Show spec differences | - -## When Implementation is Complete - -Once all tasks are marked `[x]` and validation passes: - -1. **Commit your changes** with a descriptive message referencing the change-id -2. **Create a pull request** for review -3. **Wait for deployment** before proceeding to Stage 3 (Archive) - -```bash -git add . -git commit -m "feat: implement - " -``` - -## Key Principles - -- **Complete tasks in order** - Dependencies exist for a reason -- **Verify before marking done** - Each `[x]` represents tested, working code -- **Don't shortcut** - If something is unclear, ask rather than assume -- **Keep specs in sync** - Implemented behavior should match spec deltas +### Stage 2: Implementing Changes +Track these steps as TODOs and complete them one by one. +1. **Read proposal.md** - Understand what's being built +2. **Read design.md** (if exists) - Review technical decisions +3. **Read tasks.md** - Get implementation checklist +4. **Implement tasks sequentially** - Complete in order +5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses +6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality +7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved \ No newline at end of file diff --git a/src/prompts/openspec/openspec.archive.md b/src/prompts/openspec/openspec.archive.md index 5de3eb24..760cd26f 100644 --- a/src/prompts/openspec/openspec.archive.md +++ b/src/prompts/openspec/openspec.archive.md @@ -1,162 +1,6 @@ -# OpenSpec Archive - Stage 3: Archiving Changes - -You are helping the user archive a completed OpenSpec change. This is Stage 3 of the OpenSpec workflow. - -## User Input - -```text -$ARGUMENTS -``` - -You **MUST** consider the user input before proceeding (if not empty). - -## Prerequisites - -**IMPORTANT: Archive only after deployment.** - -Stage 3 should be run after: -1. All tasks in `tasks.md` are marked complete (`- [x]`) -2. The code has been reviewed and merged -3. The changes have been deployed to production (or the target environment) - -## Archive Workflow - -### Step 1: Verify Completion - -Before archiving, confirm: - -```bash -# Check all tasks are complete -openspec show - -# Validate the change passes all checks -openspec validate --strict -``` - -### Step 2: Archive the Change - -Move the change directory to archive with a date prefix: - -```bash -openspec archive --yes -``` - -This command: -1. **Moves** `openspec/changes//` to `openspec/changes/archive/YYYY-MM-DD-/` -2. **Updates** `openspec/specs/` with any capability changes -3. **Validates** the archived change passes all checks - -### Step 3: Verify Archive Success - -After archiving: - -```bash -# Confirm change is no longer in active list -openspec list - -# Verify specs were updated correctly -openspec validate --strict -``` - -## Manual Archive Process - -If you need to archive manually (without the CLI): - -### 1. Create Archive Directory - -```bash -mkdir -p openspec/changes/archive/$(date +%Y-%m-%d)- -mv openspec/changes//* openspec/changes/archive/$(date +%Y-%m-%d)-/ -rmdir openspec/changes/ -``` - -### 2. Update Main Specs - -If the change modified existing specs, merge the deltas: - -1. **For ADDED requirements** - Copy to `openspec/specs//spec.md` -2. **For MODIFIED requirements** - Replace the original requirement with the modified version -3. **For REMOVED requirements** - Delete from `openspec/specs//spec.md` -4. **For RENAMED requirements** - Update the name in place - -### 3. Clean Up Spec Deltas - -The archived change should retain its spec deltas for historical reference, but the main `specs/` directory should now reflect the deployed state. - -## Archive Options - -```bash -# Standard archive (prompts for confirmation) -openspec archive - -# Skip confirmation prompt -openspec archive --yes -openspec archive -y - -# Skip spec updates (for tooling-only changes) -openspec archive --skip-specs -``` - -**Note:** Always pass the change-id explicitly. Don't rely on positional inference. - -## Validation After Archive - -Run comprehensive validation to confirm: - -```bash -openspec validate --strict -``` - -This validates: -- All archived changes pass validation -- Main specs are internally consistent -- No orphaned references exist - -## Common Issues - -### Spec Conflicts - -If archiving fails due to spec conflicts: - -1. **Review the conflict** - Check which requirements clash -2. **Resolve manually** - Edit `specs//spec.md` to resolve -3. **Re-run validation** - Ensure consistency after manual fixes -4. **Document resolution** - Note any manual interventions - -### Missing Archive Directory - -If the archive directory doesn't exist: - -```bash -mkdir -p openspec/changes/archive -``` - -### Partial Archive - -If archive was interrupted: - -1. **Check for duplicate files** in both `changes/` and `changes/archive/` -2. **Complete the move** manually if needed -3. **Run validation** to confirm state is correct - -## Post-Archive Checklist - -After successfully archiving: - -- [ ] Change is in `openspec/changes/archive/YYYY-MM-DD-/` -- [ ] Active change list (`openspec list`) no longer shows it -- [ ] Main specs reflect deployed state -- [ ] Validation passes (`openspec validate --strict`) -- [ ] Git commit includes the archive change - -```bash -git add . -git commit -m "chore: archive after deployment" -``` - -## Key Principles - -- **Archive promptly** - Don't let changes linger after deployment -- **Keep history** - Archived changes serve as documentation -- **Specs are truth** - After archiving, `specs/` represents deployed reality -- **Validate always** - Run validation before and after archiving +### Stage 3: Archiving Changes +After deployment, create separate PR to: +- Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/` +- Update `specs/` if capabilities changed +- Use `openspec archive --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly) +- Run `openspec validate --strict` to confirm the archived change passes checks \ No newline at end of file diff --git a/src/prompts/openspec/openspec.proposal.md b/src/prompts/openspec/openspec.proposal.md index 5726fcae..bd23c427 100644 --- a/src/prompts/openspec/openspec.proposal.md +++ b/src/prompts/openspec/openspec.proposal.md @@ -1,203 +1,31 @@ -# OpenSpec Proposal - Stage 1: Creating Changes - -You are helping the user create an OpenSpec change proposal. This is Stage 1 of the OpenSpec workflow. - -## User Input - -```text -$ARGUMENTS -``` - -You **MUST** consider the user input before proceeding (if not empty). - -## When to Create a Proposal - -Create a proposal when making substantial modifications: - -- Adding new features or functionality -- Implementing breaking changes (APIs, schemas) -- Changing architectural patterns -- Optimizing performance that affects behavior -- Updating security patterns - -**Skip proposal for:** bug fixes, typos, comments, non-breaking dependency updates, and configuration-only changes. - -## Stage 1 Workflow - -Follow these steps to create a complete change proposal: - -### Step 1: Review Existing Context - -Before creating a proposal: - -1. **Check `openspec/project.md`** for project conventions and context -2. **Run `openspec list`** to see active changes (avoid conflicts) -3. **Run `openspec list --specs`** to view existing capabilities -4. **Use `openspec show [spec]`** to review current specification state - -### Step 2: Choose a Unique Change-ID - -- Use **kebab-case** naming -- Use **verb-led** format: - - `add-` for new features - - `update-` for modifications - - `remove-` for deprecations - - `refactor-` for structural changes -- Ensure uniqueness (append `-2` or `-3` if needed) - -**Examples:** `add-user-authentication`, `update-api-v2-endpoints`, `remove-legacy-cache` - -### Step 3: Scaffold Directory Structure - -Create the following structure: - -``` -openspec/changes/[change-id]/ -├── proposal.md # What and why -├── tasks.md # Implementation checklist -├── design.md # Optional: technical decisions -└── specs/ # Spec deltas - └── [capability]/ - └── spec.md -``` - -### Step 4: Write proposal.md - -Create `proposal.md` with these sections: - -```markdown -# [Change Title] - -## Why - -1–2 sentences explaining the problem or opportunity this addresses. - -## What Changes - -- Bulleted list of modifications -- Mark breaking changes as **BREAKING** -- Be specific about affected components - -## Impact - -- List affected specs -- List affected code modules -- List affected external systems -``` - -### Step 5: Create Spec Deltas - -In `specs/[capability]/spec.md`, use operation headers: - -```markdown -## ADDED Requirements - -New, standalone capabilities that don't exist yet. - -### REQ-001: Requirement Title - -Description of the new requirement. - -#### Scenario: Success case -- **WHEN** specific conditions are met -- **THEN** expected outcome occurs - -## MODIFIED Requirements - -Changed behavior or scope of existing requirements. - -### REQ-002: Existing Requirement (Modified) - -Updated description reflecting new behavior. - -#### Scenario: Updated behavior -- **WHEN** new conditions apply -- **THEN** new outcome expected - -## REMOVED Requirements - -Deprecated features being removed. - -### REQ-003: Deprecated Feature - -**Reason:** Explain why this is being removed -**Migration:** Describe how users should migrate - -## RENAMED Requirements - -Name-only changes (no behavior change). - -### REQ-004: Old Name → New Name -``` - -**Critical Rules:** -- Include at least one `#### Scenario:` per requirement (use 4 hashtags) -- Use `**WHEN**` and `**THEN**` bullets within scenarios -- For MODIFIED requirements, copy the entire existing requirement block and edit it -- Header text must match exactly (whitespace-insensitive) when modifying - -### Step 6: Create tasks.md - -Create a structured implementation checklist: - -```markdown -# Implementation Tasks - -## Phase 1: Setup - -- [ ] T001 Initial setup task -- [ ] T002 Configuration task - -## Phase 2: Core Implementation - -- [ ] T003 Main feature implementation -- [ ] T004 Supporting functionality -- [ ] T005 Integration work - -## Phase 3: Testing & Documentation - -- [ ] T006 Add unit tests -- [ ] T007 Add integration tests -- [ ] T008 Update documentation -``` - -### Step 7: Optional - Create design.md - -Create `design.md` only when needed for: -- Cross-cutting changes affecting multiple systems -- New dependencies or external integrations -- Significant data model changes -- Security or performance complexity - -**design.md sections:** -- Context -- Goals / Non-Goals -- Key Decisions -- Risks & Trade-offs -- Migration Plan (if applicable) -- Open Questions - -### Step 8: Validate Before Sharing - -Always validate your proposal: - -```bash -openspec validate --strict -``` - -Fix all issues identified by validation. Use `openspec show --json --deltas-only` for debugging. - -## Key Principles - -- **Specs are truth** - The `specs/` directory reflects what is built -- **Changes are proposals** - The `changes/` directory contains planned modifications -- **Ask before ambiguity** - If the request is unclear, ask 1–2 clarifying questions before scaffolding - -## Helpful Commands - -| Command | Purpose | -|---------|---------| -| `openspec list` | View active changes | -| `openspec list --specs` | List existing capabilities | -| `openspec show [item]` | Display change or spec details | -| `openspec validate [item] --strict` | Comprehensive validation | +### Stage 1: Creating Changes +Create proposal when you need to: +- Add features or functionality +- Make breaking changes (API, schema) +- Change architecture or patterns +- Optimize performance (changes behavior) +- Update security patterns + +Triggers (examples): +- "Help me create a change proposal" +- "Help me plan a change" +- "Help me create a proposal" +- "I want to create a spec proposal" +- "I want to create a spec" + +Loose matching guidance: +- Contains one of: `proposal`, `change`, `spec` +- With one of: `create`, `plan`, `make`, `start`, `help` + +Skip proposal for: +- Bug fixes (restore intended behavior) +- Typos, formatting, comments +- Dependency updates (non-breaking) +- Configuration changes +- Tests for existing behavior + +**Workflow** +1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context. +2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes//`. +3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement. +4. Run `openspec validate --strict` and resolve any issues before sharing the proposal. \ No newline at end of file From 91855077d004e3a96e498f97fa3381f0ab483bdd Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 27 Dec 2025 11:50:47 -0600 Subject: [PATCH 05/15] MAESTRO: feat(openspec): add IPC handlers for OpenSpec commands Create OpenSpec IPC handler module with full parity to speckit.ts: - Add openspec:getMetadata, openspec:getPrompts, openspec:getCommand - Add openspec:savePrompt, openspec:resetPrompt, openspec:refresh - Register handlers in index.ts --- src/main/ipc/handlers/index.ts | 4 ++ src/main/ipc/handlers/openspec.ts | 100 ++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/main/ipc/handlers/openspec.ts diff --git a/src/main/ipc/handlers/index.ts b/src/main/ipc/handlers/index.ts index c4c7ecf7..da182ae2 100644 --- a/src/main/ipc/handlers/index.ts +++ b/src/main/ipc/handlers/index.ts @@ -22,6 +22,7 @@ import { registerAgentSessionsHandlers, AgentSessionsHandlerDependencies } from import { registerGroupChatHandlers, GroupChatHandlerDependencies } from './groupChat'; import { registerDebugHandlers, DebugHandlerDependencies } from './debug'; import { registerSpeckitHandlers } from './speckit'; +import { registerOpenSpecHandlers } from './openspec'; import { registerContextHandlers, ContextHandlerDependencies, cleanupAllGroomingSessions, getActiveGroomingSessionCount } from './context'; import { AgentDetector } from '../../agent-detector'; import { ProcessManager } from '../../process-manager'; @@ -45,6 +46,7 @@ export { registerAgentSessionsHandlers }; export { registerGroupChatHandlers }; export { registerDebugHandlers }; export { registerSpeckitHandlers }; +export { registerOpenSpecHandlers }; export { registerContextHandlers, cleanupAllGroomingSessions, getActiveGroomingSessionCount }; export type { AgentsHandlerDependencies }; export type { ProcessHandlerDependencies }; @@ -154,6 +156,8 @@ export function registerAllHandlers(deps: HandlerDependencies): void { }); // Register spec-kit handlers (no dependencies needed) registerSpeckitHandlers(); + // Register OpenSpec handlers (no dependencies needed) + registerOpenSpecHandlers(); registerContextHandlers({ getMainWindow: deps.getMainWindow, getProcessManager: deps.getProcessManager, diff --git a/src/main/ipc/handlers/openspec.ts b/src/main/ipc/handlers/openspec.ts new file mode 100644 index 00000000..8d669b04 --- /dev/null +++ b/src/main/ipc/handlers/openspec.ts @@ -0,0 +1,100 @@ +/** + * OpenSpec IPC Handlers + * + * Provides IPC handlers for managing OpenSpec commands: + * - Get metadata (version, last refresh date) + * - Get all commands with prompts + * - Save user edits to prompts + * - Reset prompts to bundled defaults + * - Refresh prompts from GitHub + */ + +import { ipcMain } from 'electron'; +import { logger } from '../../utils/logger'; +import { createIpcHandler, CreateHandlerOptions } from '../../utils/ipcHandler'; +import { + getOpenSpecMetadata, + getOpenSpecPrompts, + saveOpenSpecPrompt, + resetOpenSpecPrompt, + refreshOpenSpecPrompts, + getOpenSpecCommandBySlash, + OpenSpecCommand, + OpenSpecMetadata, +} from '../../openspec-manager'; + +const LOG_CONTEXT = '[OpenSpec]'; + +// Helper to create handler options with consistent context +const handlerOpts = (operation: string, logSuccess = true): CreateHandlerOptions => ({ + context: LOG_CONTEXT, + operation, + logSuccess, +}); + +/** + * Register all OpenSpec IPC handlers. + */ +export function registerOpenSpecHandlers(): void { + // Get metadata (version info, last refresh date) + ipcMain.handle( + 'openspec:getMetadata', + createIpcHandler(handlerOpts('getMetadata', false), async () => { + const metadata = await getOpenSpecMetadata(); + return { metadata }; + }) + ); + + // Get all openspec prompts + ipcMain.handle( + 'openspec:getPrompts', + createIpcHandler(handlerOpts('getPrompts', false), async () => { + const commands = await getOpenSpecPrompts(); + return { commands }; + }) + ); + + // Get a single command by slash command string + ipcMain.handle( + 'openspec:getCommand', + createIpcHandler(handlerOpts('getCommand', false), async (slashCommand: string) => { + const command = await getOpenSpecCommandBySlash(slashCommand); + return { command }; + }) + ); + + // Save user's edit to a prompt + ipcMain.handle( + 'openspec:savePrompt', + createIpcHandler(handlerOpts('savePrompt'), async (id: string, content: string) => { + await saveOpenSpecPrompt(id, content); + logger.info(`Saved custom prompt for openspec.${id}`, LOG_CONTEXT); + return {}; + }) + ); + + // Reset a prompt to bundled default + ipcMain.handle( + 'openspec:resetPrompt', + createIpcHandler(handlerOpts('resetPrompt'), async (id: string) => { + const prompt = await resetOpenSpecPrompt(id); + logger.info(`Reset openspec.${id} to bundled default`, LOG_CONTEXT); + return { prompt }; + }) + ); + + // Refresh prompts from GitHub + ipcMain.handle( + 'openspec:refresh', + createIpcHandler(handlerOpts('refresh'), async () => { + const metadata = await refreshOpenSpecPrompts(); + logger.info(`Refreshed OpenSpec prompts to commit ${metadata.commitSha}`, LOG_CONTEXT); + return { metadata }; + }) + ); + + logger.debug(`${LOG_CONTEXT} OpenSpec IPC handlers registered`); +} + +// Export types for preload +export type { OpenSpecCommand, OpenSpecMetadata }; From d7f325283b30a65d32ef79e088713cc7a5b6efce Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 27 Dec 2025 11:53:14 -0600 Subject: [PATCH 06/15] MAESTRO: feat(openspec): add preload bridge API for OpenSpec commands Add openspec API section to preload.ts contextBridge with all 6 methods (getMetadata, getPrompts, getCommand, savePrompt, resetPrompt, refresh) and corresponding MaestroAPI interface types. Mirrors speckit pattern. --- src/main/preload.ts | 124 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/src/main/preload.ts b/src/main/preload.ts index 7fb00a24..319467b2 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -983,6 +983,75 @@ contextBridge.exposeInMainWorld('maestro', { }>, }, + // OpenSpec API (bundled OpenSpec slash commands) + openspec: { + // Get metadata (version, last refresh date) + getMetadata: () => + ipcRenderer.invoke('openspec:getMetadata') as Promise<{ + success: boolean; + metadata?: { + lastRefreshed: string; + commitSha: string; + sourceVersion: string; + sourceUrl: string; + }; + error?: string; + }>, + // Get all openspec prompts + getPrompts: () => + ipcRenderer.invoke('openspec:getPrompts') as Promise<{ + success: boolean; + commands?: Array<{ + id: string; + command: string; + description: string; + prompt: string; + isCustom: boolean; + isModified: boolean; + }>; + error?: string; + }>, + // Get a single command by slash command string + getCommand: (slashCommand: string) => + ipcRenderer.invoke('openspec:getCommand', slashCommand) as Promise<{ + success: boolean; + command?: { + id: string; + command: string; + description: string; + prompt: string; + isCustom: boolean; + isModified: boolean; + } | null; + error?: string; + }>, + // Save user's edit to a prompt + savePrompt: (id: string, content: string) => + ipcRenderer.invoke('openspec:savePrompt', id, content) as Promise<{ + success: boolean; + error?: string; + }>, + // Reset a prompt to bundled default + resetPrompt: (id: string) => + ipcRenderer.invoke('openspec:resetPrompt', id) as Promise<{ + success: boolean; + prompt?: string; + error?: string; + }>, + // Refresh prompts from GitHub + refresh: () => + ipcRenderer.invoke('openspec:refresh') as Promise<{ + success: boolean; + metadata?: { + lastRefreshed: string; + commitSha: string; + sourceVersion: string; + sourceUrl: string; + }; + error?: string; + }>, + }, + // Notification API notification: { show: (title: string, body: string) => @@ -2359,6 +2428,61 @@ export interface MaestroAPI { error?: string; }>; }; + openspec: { + getMetadata: () => Promise<{ + success: boolean; + metadata?: { + lastRefreshed: string; + commitSha: string; + sourceVersion: string; + sourceUrl: string; + }; + error?: string; + }>; + getPrompts: () => Promise<{ + success: boolean; + commands?: Array<{ + id: string; + command: string; + description: string; + prompt: string; + isCustom: boolean; + isModified: boolean; + }>; + error?: string; + }>; + getCommand: (slashCommand: string) => Promise<{ + success: boolean; + command?: { + id: string; + command: string; + description: string; + prompt: string; + isCustom: boolean; + isModified: boolean; + } | null; + error?: string; + }>; + savePrompt: (id: string, content: string) => Promise<{ + success: boolean; + error?: string; + }>; + resetPrompt: (id: string) => Promise<{ + success: boolean; + prompt?: string; + error?: string; + }>; + refresh: () => Promise<{ + success: boolean; + metadata?: { + lastRefreshed: string; + commitSha: string; + sourceVersion: string; + sourceUrl: string; + }; + error?: string; + }>; + }; } declare global { From 546978c715841f93e0fa85ee83f3dc957fa612b4 Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 27 Dec 2025 11:56:32 -0600 Subject: [PATCH 07/15] MAESTRO: feat(openspec): add renderer service and TypeScript types - Created src/renderer/services/openspec.ts with 3 exported functions: - getOpenSpecCommands() - Returns all OpenSpec commands - getOpenSpecMetadata() - Returns metadata or null - getOpenSpecCommand() - Returns single command by slash string - Added OpenSpecCommand and OpenSpecMetadata interfaces to types/index.ts - Extended MaestroAPI in global.d.ts with openspec API (6 methods) - TypeScript lint passes successfully --- src/renderer/global.d.ts | 55 ++++++++++++++++++++++++++++++ src/renderer/services/openspec.ts | 56 +++++++++++++++++++++++++++++++ src/renderer/types/index.ts | 18 ++++++++++ 3 files changed, 129 insertions(+) create mode 100644 src/renderer/services/openspec.ts diff --git a/src/renderer/global.d.ts b/src/renderer/global.d.ts index 966e1924..e291ea17 100644 --- a/src/renderer/global.d.ts +++ b/src/renderer/global.d.ts @@ -1246,6 +1246,61 @@ interface MaestroAPI { error?: string; }>; }; + openspec: { + getMetadata: () => Promise<{ + success: boolean; + metadata?: { + lastRefreshed: string; + commitSha: string; + sourceVersion: string; + sourceUrl: string; + }; + error?: string; + }>; + getPrompts: () => Promise<{ + success: boolean; + commands?: Array<{ + id: string; + command: string; + description: string; + prompt: string; + isCustom: boolean; + isModified: boolean; + }>; + error?: string; + }>; + getCommand: (slashCommand: string) => Promise<{ + success: boolean; + command?: { + id: string; + command: string; + description: string; + prompt: string; + isCustom: boolean; + isModified: boolean; + }; + error?: string; + }>; + savePrompt: (id: string, content: string) => Promise<{ + success: boolean; + error?: string; + }>; + resetPrompt: (id: string) => Promise<{ + success: boolean; + prompt?: string; + error?: string; + }>; + refresh: () => Promise<{ + success: boolean; + metadata?: { + lastRefreshed: string; + commitSha: string; + sourceVersion: string; + sourceUrl: string; + }; + error?: string; + }>; + }; } declare global { diff --git a/src/renderer/services/openspec.ts b/src/renderer/services/openspec.ts new file mode 100644 index 00000000..32345771 --- /dev/null +++ b/src/renderer/services/openspec.ts @@ -0,0 +1,56 @@ +/** + * OpenSpec Service + * + * Provides access to bundled OpenSpec commands for the renderer. + * These commands integrate with the slash command system. + */ + +import type { OpenSpecCommand, OpenSpecMetadata } from '../types'; + +/** + * Get all OpenSpec commands from the main process + */ +export async function getOpenSpecCommands(): Promise { + try { + const result = await window.maestro.openspec.getPrompts(); + if (result.success && result.commands) { + return result.commands; + } + return []; + } catch (error) { + console.error('[OpenSpec] Failed to get commands:', error); + return []; + } +} + +/** + * Get OpenSpec metadata (version, refresh date) + */ +export async function getOpenSpecMetadata(): Promise { + try { + const result = await window.maestro.openspec.getMetadata(); + if (result.success && result.metadata) { + return result.metadata; + } + return null; + } catch (error) { + console.error('[OpenSpec] Failed to get metadata:', error); + return null; + } +} + +/** + * Get a single OpenSpec command by its slash command string + */ +export async function getOpenSpecCommand(slashCommand: string): Promise { + try { + const result = await window.maestro.openspec.getCommand(slashCommand); + if (result.success && result.command) { + return result.command; + } + return null; + } catch (error) { + console.error('[OpenSpec] Failed to get command:', error); + return null; + } +} diff --git a/src/renderer/types/index.ts b/src/renderer/types/index.ts index 482c9a23..832b5f46 100644 --- a/src/renderer/types/index.ts +++ b/src/renderer/types/index.ts @@ -585,6 +585,24 @@ export interface SpecKitMetadata { sourceUrl: string; // GitHub repo URL } +// OpenSpec command definition (bundled from Fission-AI/OpenSpec) +export interface OpenSpecCommand { + id: string; // e.g., 'proposal' + command: string; // e.g., '/openspec.proposal' + description: string; + prompt: string; + isCustom: boolean; // true for 'help' and 'implement' (Maestro-specific) + isModified: boolean; // true if user has edited +} + +// OpenSpec metadata for tracking version and refresh status +export interface OpenSpecMetadata { + lastRefreshed: string; // ISO date + commitSha: string; // Git commit SHA or version tag + sourceVersion: string; // Semantic version + sourceUrl: string; // GitHub repo URL +} + // Leaderboard registration data for runmaestro.ai integration export interface LeaderboardRegistration { // Required fields From 3986770df62389128f8153a5ce80b28b48244546 Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Sat, 27 Dec 2025 11:59:43 -0600 Subject: [PATCH 08/15] MAESTRO: feat(openspec): add OpenSpec commands panel UI component Create OpenSpecCommandsPanel.tsx mirroring SpecKitCommandsPanel pattern: - Uses GitBranch icon to differentiate from Spec Kit's Wand2 - Links to Fission-AI/OpenSpec GitHub repository - Supports expand/collapse, edit, reset, and refresh operations - Shows "Maestro" badge for custom commands, "Modified" for edited - Uses template autocomplete for prompt editing --- .../components/OpenSpecCommandsPanel.tsx | 376 ++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 src/renderer/components/OpenSpecCommandsPanel.tsx diff --git a/src/renderer/components/OpenSpecCommandsPanel.tsx b/src/renderer/components/OpenSpecCommandsPanel.tsx new file mode 100644 index 00000000..17a7612e --- /dev/null +++ b/src/renderer/components/OpenSpecCommandsPanel.tsx @@ -0,0 +1,376 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Edit2, Save, X, RotateCcw, RefreshCw, ExternalLink, ChevronDown, ChevronRight, GitBranch } from 'lucide-react'; +import type { Theme, OpenSpecCommand, OpenSpecMetadata } from '../types'; +import { useTemplateAutocomplete } from '../hooks'; +import { TemplateAutocompleteDropdown } from './TemplateAutocompleteDropdown'; + +interface OpenSpecCommandsPanelProps { + theme: Theme; +} + +interface EditingCommand { + id: string; + prompt: string; +} + +export function OpenSpecCommandsPanel({ theme }: OpenSpecCommandsPanelProps) { + const [commands, setCommands] = useState([]); + const [metadata, setMetadata] = useState(null); + const [editingCommand, setEditingCommand] = useState(null); + const [isRefreshing, setIsRefreshing] = useState(false); + const [expandedCommands, setExpandedCommands] = useState>(new Set()); + const [isLoading, setIsLoading] = useState(true); + + const editCommandTextareaRef = useRef(null); + + // Template autocomplete for edit command prompt + const { + autocompleteState: editAutocompleteState, + handleKeyDown: handleEditAutocompleteKeyDown, + handleChange: handleEditAutocompleteChange, + selectVariable: selectEditVariable, + autocompleteRef: editAutocompleteRef, + } = useTemplateAutocomplete({ + textareaRef: editCommandTextareaRef, + value: editingCommand?.prompt || '', + onChange: (value) => editingCommand && setEditingCommand({ ...editingCommand, prompt: value }), + }); + + // Load commands and metadata on mount + useEffect(() => { + const loadData = async () => { + setIsLoading(true); + try { + const [promptsResult, metadataResult] = await Promise.all([ + window.maestro.openspec.getPrompts(), + window.maestro.openspec.getMetadata(), + ]); + + if (promptsResult.success && promptsResult.commands) { + setCommands(promptsResult.commands); + } + if (metadataResult.success && metadataResult.metadata) { + setMetadata(metadataResult.metadata); + } + } catch (error) { + console.error('Failed to load openspec commands:', error); + } finally { + setIsLoading(false); + } + }; + + loadData(); + }, []); + + const handleSaveEdit = async () => { + if (!editingCommand) return; + + try { + const result = await window.maestro.openspec.savePrompt(editingCommand.id, editingCommand.prompt); + if (result.success) { + setCommands(commands.map(cmd => + cmd.id === editingCommand.id + ? { ...cmd, prompt: editingCommand.prompt, isModified: true } + : cmd + )); + setEditingCommand(null); + } + } catch (error) { + console.error('Failed to save prompt:', error); + } + }; + + const handleReset = async (id: string) => { + try { + const result = await window.maestro.openspec.resetPrompt(id); + if (result.success && result.prompt) { + setCommands(commands.map(cmd => + cmd.id === id + ? { ...cmd, prompt: result.prompt!, isModified: false } + : cmd + )); + } + } catch (error) { + console.error('Failed to reset prompt:', error); + } + }; + + const handleRefresh = async () => { + setIsRefreshing(true); + try { + const result = await window.maestro.openspec.refresh(); + if (result.success && result.metadata) { + setMetadata(result.metadata); + // Reload prompts after refresh + const promptsResult = await window.maestro.openspec.getPrompts(); + if (promptsResult.success && promptsResult.commands) { + setCommands(promptsResult.commands); + } + } + } catch (error) { + console.error('Failed to refresh openspec prompts:', error); + } finally { + setIsRefreshing(false); + } + }; + + const handleCancelEdit = () => { + setEditingCommand(null); + }; + + const toggleExpanded = (id: string) => { + const newExpanded = new Set(expandedCommands); + if (newExpanded.has(id)) { + newExpanded.delete(id); + } else { + newExpanded.add(id); + } + setExpandedCommands(newExpanded); + }; + + const formatDate = (isoDate: string) => { + try { + return new Date(isoDate).toLocaleDateString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric', + }); + } catch { + return isoDate; + } + }; + + if (isLoading) { + return ( +
+
+ +

+ Loading OpenSpec commands... +

+
+
+ ); + } + + return ( +
+
+ +

+ Change management commands from{' '} + + {' '}for structured change proposals. +

+
+ + {/* Metadata and refresh */} + {metadata && ( +
+
+ Version: + + {metadata.sourceVersion} + + + Updated: + + {formatDate(metadata.lastRefreshed)} + +
+ +
+ )} + + {/* Commands list */} +
+ {commands.map((cmd) => ( +
+ {editingCommand?.id === cmd.id ? ( + // Editing mode +
+
+ + {cmd.command} + +
+ + +
+
+
+