--- name: openspec-export-spec description: Export a feature as a compact, portable spec that an AI agent (Copilot, Claude) on a sandboxed machine can implement from. Optimized for minimal hand-typing. license: MIT compatibility: Requires openspec CLI. metadata: author: openspec version: "1.0" generatedBy: "1.2.0" --- Export a feature as a portable spec for AI-assisted reimplementation on a sandboxed machine. Instead of retyping code, you retype a compact spec. The AI on the sandbox (Copilot, Claude, OpenSpec) generates the code from the spec. --- **Input**: A change name (active or archived), or a description of the feature. If omitted, prompt for selection. **Steps** 1. **Identify the source feature** Same as `openspec-extract-feature` step 1: - Check active changes and archive for the change name - If not found, prompt with **AskUserQuestion tool** - Read all artifacts: `proposal.md`, `design.md`, `tasks.md`, specs 2. **Analyze dependency chain (cumulative mode)** Features often build on each other. Before generating the spec, determine what the target feature depends on. a. **Read all archived change proposals** in `openspec/changes/archive/` (sorted by date). For each, read `proposal.md` to understand what it adds and what it depends on. b. **Build a dependency graph**: - Which changes does the target feature require? - Which earlier changes are superseded? (e.g., if Change 6 replaces Change 3's implementation, include Change 6's version, not Change 3's) - Which changes are unrelated and can be skipped? c. **Ask the user** using **AskUserQuestion tool**: > "This feature depends on earlier changes. How should I scope the spec?" > > Options: > 1. **Cumulative** — include all foundation this feature needs (recommended if target is a fresh codebase) > 2. **Delta only** — just what this specific change adds (use if target already has the foundation) > 3. **Custom** — let me pick which dependencies to include Show the dependency chain so the user can decide. d. **In cumulative mode**: The spec covers the entire stack from foundation to feature. - Merge packages, components, wiring from all included changes - Skip superseded components (use the latest version) - The spec should read as a single coherent feature, not a list of changes e. **In delta mode**: The spec covers only the selected change. - Add a "Assumes" section listing what must already exist in the target 3. **Read the actual implementation** Read all source files that were created or modified by this feature (and its dependencies if cumulative). The spec must reflect what was actually built, not just what was planned. 4. **Determine the target context** Use **AskUserQuestion tool** to ask: > "Tell me about the target codebase: > 1. Project name / root namespace > 2. Existing stack (ASP.NET Core? Blazor? MudBlazor?) > 3. Does it already have any of these? (controllers, DI setup, chat endpoint) > 4. Does the target have OpenSpec? GitHub Copilot? Claude Code?" This shapes what the spec assumes vs what it must specify. 5. **Generate the portable spec** Create a single markdown document that is: - **Compact**: Target ~30-50 lines for a medium feature - **Precise**: Unambiguous enough for an AI to implement correctly - **Self-contained**: No references to external files or repos - **Stack-aware**: Uses the right terminology for the target stack Structure: ```markdown # Feature: ## Target: () ## Packages ## Architecture <2-3 sentence overview of how the pieces fit together> ## Components ### : - - - ### : ... ## Contracts ## Wiring ## Behavior ``` **Compression strategies:** - Use bullet points, not prose - Specify contracts precisely (field names, types, API shapes) - Let the AI infer standard patterns (error handling, null checks, etc.) - Only specify non-obvious behavior (the surprising parts) - Omit anything the AI would do by default for the given stack 6. **Estimate typing effort** Count characters in the spec. Compare to the code recipe equivalent. Show the compression ratio: ``` Code recipe: ~120 lines to type This spec: ~35 lines to type Compression: 3.4x ``` 7. **Optionally generate an OpenSpec-compatible version** If the target has OpenSpec, also generate a version structured as: - A `proposal.md` (minimal — 5-10 lines) - A `tasks.md` (implementation steps the target AI follows) These can be even more compact because OpenSpec provides the scaffolding. Save this variant alongside the main spec. 8. **Write the output** Save to: `openspec/exports/-spec.md` If OpenSpec variant: `openspec/exports/-openspec.md` Display the full content for review. **Output Format** ```markdown # Feature: Semantic Kernel Chat with Tool Calling ## Target: ApplicationX (ASP.NET Core + Blazor WASM + MudBlazor) ## Packages - Microsoft.SemanticKernel 1.74.0 - Microsoft.SemanticKernel.Connectors.OpenAI 1.74.0 ## Architecture POST /api/chat endpoint accepts messages, runs them through SK's chat completion with auto tool calling enabled, streams response as SSE. An ExtractionPlugin validates structured data extracted by the LLM. ## Components ### ChatController: Controllers/ChatController.cs - POST endpoint, injects Kernel via DI - Converts ChatMessage[] to SK ChatHistory - Streams via GetStreamingChatMessageContentsAsync - Outputs SSE: `data: {"text":"..."}\n\n` then `data: [DONE]\n\n` ### ExtractionPlugin: Plugins/ExtractionPlugin.cs - [KernelFunction("validate_extracted_fields")] - Accepts JSON string, deserializes to ExtractedFields - Returns {"isValid": bool, "errors": string[]} ### ExtractedFields: Models/ExtractedFields.cs - Required: Client(string), Project(string), Hours(decimal), Rate(decimal), Currency(string), Date(string) - Optional: Description(string), PoNumber(string) ### ValidationResult: Models/ValidationResult.cs - IsValid(bool), Errors(List) ### ChatRequest/ChatMessage: Shared/Models/ - ChatRequest: Messages(List) - ChatMessage: Role(string), Content(string) ## Wiring (Program.cs, add after AddControllers) - AddOpenAIChatCompletion(model, endpoint with /v1 suffix, apiKey) - AddKernel() - AddSingleton() - UseCors for Blazor client origin ## Behavior - FunctionChoiceBehavior.Auto() enables autonomous tool calling - SK's built-in limit prevents runaway tool call loops - Plugin imported per-request via _kernel.ImportPluginFromObject - Base URL must include /v1 — OpenAI SDK appends chat/completions directly ``` **Lines to type: ~35 | Code equivalent: ~150 lines | Compression: 4.3x** **Guardrails** - Prioritize precision over brevity — an ambiguous spec wastes more time than a slightly longer one - Always include exact field names, types, and API shapes — these are the hardest to guess - Include non-obvious gotchas (like the /v1 base URL requirement) - Test the spec mentally: could an AI implement this correctly without seeing the original code? - If the feature is too complex for a single spec page (~50+ lines), split into multiple specs by component - Always show the compression ratio so the user can decide between spec and code recipe - The spec must be readable when printed in monospace — no wide tables or long lines - In cumulative mode, the spec must read as one coherent feature — not a list of sequential changes - Skip superseded components — always describe the latest version of each piece - In delta mode, add an "Assumes" section so the target AI knows what must already exist - In the output header, note which changes were included and which were skipped