Spaces:
Running
Running
zhengyi Copilot commited on
Commit ·
ba681c7
0
Parent(s):
feat: 實現深色模式功能
Browse files- 新增主題切換 UI 按鈕
- 實現主題持久化儲存 (localStorage)
- 支援亮色/深色主題樣式
- 適配 Plotly 圖表主題
- WCAG AA 無障礙標準
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This view is limited to 50 files because it contains too many changes. See raw diff
- .claude/skills/skill-creator/SKILL.md +97 -0
- .github/agents/azure_task_implementer.agent.md +1 -0
- .github/agents/azure_task_plan.agent.md +1 -0
- .github/agents/azure_task_specify.agent.md +5 -0
- .github/copilot-instructions.md +58 -0
- .github/prompts/azure_task_specify.prompt.md +13 -0
- .github/prompts/opsx-apply.prompt.md +149 -0
- .github/prompts/opsx-archive.prompt.md +154 -0
- .github/prompts/opsx-bulk-archive.prompt.md +239 -0
- .github/prompts/opsx-continue.prompt.md +111 -0
- .github/prompts/opsx-explore.prompt.md +170 -0
- .github/prompts/opsx-ff.prompt.md +94 -0
- .github/prompts/opsx-new.prompt.md +66 -0
- .github/prompts/opsx-onboard.prompt.md +547 -0
- .github/prompts/opsx-sync.prompt.md +131 -0
- .github/prompts/opsx-verify.prompt.md +161 -0
- .github/skills/openspec-apply-change/SKILL.md +156 -0
- .github/skills/openspec-archive-change/SKILL.md +114 -0
- .github/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- .github/skills/openspec-continue-change/SKILL.md +118 -0
- .github/skills/openspec-explore/SKILL.md +288 -0
- .github/skills/openspec-ff-change/SKILL.md +101 -0
- .github/skills/openspec-new-change/SKILL.md +74 -0
- .github/skills/openspec-onboard/SKILL.md +554 -0
- .github/skills/openspec-sync-specs/SKILL.md +138 -0
- .github/skills/openspec-verify-change/SKILL.md +168 -0
- .vscode/mcp.json +3 -0
- .vscode/settings.json +7 -0
- ChatMemo.txt +103 -0
- DARK_MODE_GUIDE.md +90 -0
- StockList.xlsx +0 -0
- __pycache__/app.cpython-313.pyc +0 -0
- app.py +0 -0
- app_batch.py +1104 -0
- css/dark_mode.css +231 -0
- docs/specify/plan/專案名稱/SDS.md +0 -0
- docs/specify/plan/專案名稱/SRS.md +0 -0
- docs/specify/plan/專案名稱/spec.md +0 -0
- docs/specify/plan/專案名稱/tasks.md +1 -0
- docs/specify/plan/放AI產生的plan文件 +0 -0
- docs/specify/templates/放AI參考的文件格式 +0 -0
- openspec/changes/archive/2026-03-10-add-dark-mode/.openspec.yaml +2 -0
- openspec/changes/archive/2026-03-10-add-dark-mode/design.md +89 -0
- openspec/changes/archive/2026-03-10-add-dark-mode/proposal.md +34 -0
- openspec/changes/archive/2026-03-10-add-dark-mode/specs/chart-theme-support/spec.md +49 -0
- openspec/changes/archive/2026-03-10-add-dark-mode/specs/dark-mode-styling/spec.md +45 -0
- openspec/changes/archive/2026-03-10-add-dark-mode/specs/theme-persistence/spec.md +45 -0
- openspec/changes/archive/2026-03-10-add-dark-mode/specs/theme-toggle-ui/spec.md +45 -0
- openspec/changes/archive/2026-03-10-add-dark-mode/tasks.md +53 -0
- openspec/config.yaml +19 -0
.claude/skills/skill-creator/SKILL.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: skill-creator
|
| 3 |
+
description: Guide for creating effective skills. Use when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
|
| 4 |
+
source: anthropics/skills
|
| 5 |
+
license: Apache-2.0
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
# Skill Creator
|
| 9 |
+
|
| 10 |
+
Skills are modular packages that extend Claude's capabilities by providing specialized knowledge, workflows, and tools.
|
| 11 |
+
|
| 12 |
+
## Core Principles
|
| 13 |
+
|
| 14 |
+
### Concise is Key
|
| 15 |
+
The context window is a shared resource. Only add context Claude doesn't already have. Challenge each piece: "Does Claude really need this?"
|
| 16 |
+
|
| 17 |
+
### Anatomy of a Skill
|
| 18 |
+
|
| 19 |
+
```
|
| 20 |
+
skill-name/
|
| 21 |
+
├── SKILL.md (required)
|
| 22 |
+
│ ├── YAML frontmatter (name, description)
|
| 23 |
+
│ └── Markdown instructions
|
| 24 |
+
└── Bundled Resources (optional)
|
| 25 |
+
├── scripts/ - Executable code
|
| 26 |
+
├── references/ - Documentation
|
| 27 |
+
└── assets/ - Templates, images
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
### SKILL.md Format
|
| 31 |
+
|
| 32 |
+
```markdown
|
| 33 |
+
---
|
| 34 |
+
name: my-skill-name
|
| 35 |
+
description: A clear description of what this skill does and when to use it
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
# My Skill Name
|
| 39 |
+
|
| 40 |
+
[Instructions for Claude when this skill is active]
|
| 41 |
+
|
| 42 |
+
## Examples
|
| 43 |
+
- Example usage 1
|
| 44 |
+
- Example usage 2
|
| 45 |
+
|
| 46 |
+
## Guidelines
|
| 47 |
+
- Guideline 1
|
| 48 |
+
- Guideline 2
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
## Skill Creation Process
|
| 52 |
+
|
| 53 |
+
### Step 1: Understand with Examples
|
| 54 |
+
Gather concrete examples of how the skill will be used. Ask:
|
| 55 |
+
- "What functionality should this skill support?"
|
| 56 |
+
- "What would a user say that should trigger this skill?"
|
| 57 |
+
|
| 58 |
+
### Step 2: Plan Reusable Contents
|
| 59 |
+
Analyze examples to identify:
|
| 60 |
+
- **Scripts**: Code that gets rewritten repeatedly
|
| 61 |
+
- **References**: Documentation Claude needs to reference
|
| 62 |
+
- **Assets**: Templates, images for output
|
| 63 |
+
|
| 64 |
+
### Step 3: Initialize
|
| 65 |
+
Create the skill directory structure with SKILL.md and resource folders.
|
| 66 |
+
|
| 67 |
+
### Step 4: Implement
|
| 68 |
+
- Start with reusable resources (scripts, references, assets)
|
| 69 |
+
- Write clear SKILL.md with proper frontmatter
|
| 70 |
+
- Test scripts by actually running them
|
| 71 |
+
|
| 72 |
+
### Step 5: Iterate
|
| 73 |
+
Use the skill on real tasks, notice struggles, improve.
|
| 74 |
+
|
| 75 |
+
## Progressive Disclosure
|
| 76 |
+
|
| 77 |
+
Keep SKILL.md under 500 lines. Split content:
|
| 78 |
+
|
| 79 |
+
```markdown
|
| 80 |
+
# PDF Processing
|
| 81 |
+
|
| 82 |
+
## Quick start
|
| 83 |
+
[code example]
|
| 84 |
+
|
| 85 |
+
## Advanced features
|
| 86 |
+
- **Form filling**: See [FORMS.md](FORMS.md)
|
| 87 |
+
- **API reference**: See [REFERENCE.md](REFERENCE.md)
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
## What NOT to Include
|
| 91 |
+
|
| 92 |
+
- README.md
|
| 93 |
+
- INSTALLATION_GUIDE.md
|
| 94 |
+
- CHANGELOG.md
|
| 95 |
+
- User-facing documentation
|
| 96 |
+
|
| 97 |
+
Skills are for AI agents, not humans.
|
.github/agents/azure_task_implementer.agent.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# StockRecommender - AI Coding Agent ���� specify
|
.github/agents/azure_task_plan.agent.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# StockRecommender - AI Coding Agent ���� specify
|
.github/agents/azure_task_specify.agent.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# StockRecommender - AI Coding Agent ���� specify
|
| 2 |
+
|
| 3 |
+
### ����ԲӸ��
|
| 4 |
+
|
| 5 |
+
1.**���Task ���㤺�e**�G
|
.github/copilot-instructions.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# StockRecommender - AI Coding Agent Instructions
|
| 2 |
+
|
| 3 |
+
## Language
|
| 4 |
+
**��ҡB�^�СB���ͤ��B���ѡA�Ф@�ߨϥ��c�餤��A�èϥΥx�W�`�ε��J�C
|
| 5 |
+
|
| 6 |
+
## Project Overview
|
| 7 |
+
**StockRecommender** is a Python-based AI stock analysis tool designed to run on **Hugging Face Spaces** or locally. It uses **Gradio** for the web interface and leverages **yfinance** for market data and **Transformers** (FinBERT, BART) for sentiment analysis and summarization.
|
| 8 |
+
|
| 9 |
+
## Architecture & Key Files
|
| 10 |
+
- **`app_batch.py`**: The main application file with advanced features.
|
| 11 |
+
- **Batch Analysis**: Supports analyzing multiple stocks via text input or file.
|
| 12 |
+
- **Visualization**: Uses Plotly for interactive charts (Bar, Scatter, Radar, Pie).
|
| 13 |
+
- **Tabs**: Separates "Single Stock Analysis" and "Batch Stock Analysis".
|
| 14 |
+
- **`app.py`**: A lighter/older version of the application, primarily for single stock analysis.
|
| 15 |
+
- **`requirements.txt`**: Lists all Python dependencies.
|
| 16 |
+
- **`StockList.xlsx`**: (Optional) Source for batch analysis if configured.
|
| 17 |
+
|
| 18 |
+
## Environment & Setup
|
| 19 |
+
- **Hugging Face Spaces**: The app detects if it's running in HF Spaces via `IS_HUGGINGFACE_SPACE` (checks for `SPACE_ID` env var).
|
| 20 |
+
- **Runtime Package Installation**: The code includes a `install_package` function to auto-install missing dependencies at runtime. This is a specific pattern to ensure robustness in different environments.
|
| 21 |
+
|
| 22 |
+
## Core Components
|
| 23 |
+
### StockAnalyzer Class
|
| 24 |
+
- Handles data fetching via `yfinance`.
|
| 25 |
+
- Performs technical analysis (Moving Averages, RSI, MACD).
|
| 26 |
+
- Integrates AI models for sentiment analysis (if available).
|
| 27 |
+
|
| 28 |
+
### AI Models
|
| 29 |
+
- **Sentiment**: `ProsusAI/finbert` (fallback: `nlptown/bert-base-multilingual-uncased-sentiment`).
|
| 30 |
+
- **Summarization**: `facebook/bart-large-cnn` (fallback: `sshleifer/distilbart-cnn-12-6`).
|
| 31 |
+
- **Fallback**: If models fail to load (e.g., memory constraints), the app falls back to lightweight internal analysis methods.
|
| 32 |
+
|
| 33 |
+
## Developer Workflows
|
| 34 |
+
- **Running Locally**:
|
| 35 |
+
```bash
|
| 36 |
+
python app_batch.py
|
| 37 |
+
```
|
| 38 |
+
- **Dependencies**:
|
| 39 |
+
Always update `requirements.txt` when adding new packages, even though runtime installation exists.
|
| 40 |
+
```bash
|
| 41 |
+
pip install -r requirements.txt
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
## Coding Conventions
|
| 45 |
+
- **Error Handling**: Wrap external API calls (yfinance, HF pipeline) in `try-except` blocks to prevent app crashes.
|
| 46 |
+
- **UI Updates**: When modifying the UI, ensure Gradio components are properly nested in `gr.Row()` and `gr.Column()` for layout control.
|
| 47 |
+
- **Data Visualization**: Use `plotly.graph_objects` and `plotly.express` for charts; return them as `gr.Plot` objects.
|
| 48 |
+
|
| 49 |
+
## Common Patterns
|
| 50 |
+
- **Runtime Import Checks**:
|
| 51 |
+
```python
|
| 52 |
+
try:
|
| 53 |
+
import some_package
|
| 54 |
+
except ImportError:
|
| 55 |
+
install_package("some_package")
|
| 56 |
+
```
|
| 57 |
+
- **Gradio Event Listeners**:
|
| 58 |
+
Use `.click()` or `.change()` to bind functions to UI elements.
|
.github/prompts/azure_task_specify.prompt.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
agent: azure_task_specify
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
# User Request
|
| 6 |
+
�Ш�U�ڶi��ݨD����P�W��w�q�C
|
| 7 |
+
|
| 8 |
+
Task ID / Context:
|
| 9 |
+
```text
|
| 10 |
+
$ARGUMENTS
|
| 11 |
+
$SELECTION
|
| 12 |
+
````
|
| 13 |
+
|
.github/prompts/opsx-apply.prompt.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Implement tasks from an OpenSpec change (Experimental)
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Implement tasks from an OpenSpec change.
|
| 6 |
+
|
| 7 |
+
**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 8 |
+
|
| 9 |
+
**Steps**
|
| 10 |
+
|
| 11 |
+
1. **Select the change**
|
| 12 |
+
|
| 13 |
+
If a name is provided, use it. Otherwise:
|
| 14 |
+
- Infer from conversation context if the user mentioned a change
|
| 15 |
+
- Auto-select if only one active change exists
|
| 16 |
+
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
| 17 |
+
|
| 18 |
+
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
| 19 |
+
|
| 20 |
+
2. **Check status to understand the schema**
|
| 21 |
+
```bash
|
| 22 |
+
openspec status --change "<name>" --json
|
| 23 |
+
```
|
| 24 |
+
Parse the JSON to understand:
|
| 25 |
+
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
| 26 |
+
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
| 27 |
+
|
| 28 |
+
3. **Get apply instructions**
|
| 29 |
+
|
| 30 |
+
```bash
|
| 31 |
+
openspec instructions apply --change "<name>" --json
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
This returns:
|
| 35 |
+
- Context file paths (varies by schema)
|
| 36 |
+
- Progress (total, complete, remaining)
|
| 37 |
+
- Task list with status
|
| 38 |
+
- Dynamic instruction based on current state
|
| 39 |
+
|
| 40 |
+
**Handle states:**
|
| 41 |
+
- If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue`
|
| 42 |
+
- If `state: "all_done"`: congratulate, suggest archive
|
| 43 |
+
- Otherwise: proceed to implementation
|
| 44 |
+
|
| 45 |
+
4. **Read context files**
|
| 46 |
+
|
| 47 |
+
Read the files listed in `contextFiles` from the apply instructions output.
|
| 48 |
+
The files depend on the schema being used:
|
| 49 |
+
- **spec-driven**: proposal, specs, design, tasks
|
| 50 |
+
- Other schemas: follow the contextFiles from CLI output
|
| 51 |
+
|
| 52 |
+
5. **Show current progress**
|
| 53 |
+
|
| 54 |
+
Display:
|
| 55 |
+
- Schema being used
|
| 56 |
+
- Progress: "N/M tasks complete"
|
| 57 |
+
- Remaining tasks overview
|
| 58 |
+
- Dynamic instruction from CLI
|
| 59 |
+
|
| 60 |
+
6. **Implement tasks (loop until done or blocked)**
|
| 61 |
+
|
| 62 |
+
For each pending task:
|
| 63 |
+
- Show which task is being worked on
|
| 64 |
+
- Make the code changes required
|
| 65 |
+
- Keep changes minimal and focused
|
| 66 |
+
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
| 67 |
+
- Continue to next task
|
| 68 |
+
|
| 69 |
+
**Pause if:**
|
| 70 |
+
- Task is unclear → ask for clarification
|
| 71 |
+
- Implementation reveals a design issue → suggest updating artifacts
|
| 72 |
+
- Error or blocker encountered → report and wait for guidance
|
| 73 |
+
- User interrupts
|
| 74 |
+
|
| 75 |
+
7. **On completion or pause, show status**
|
| 76 |
+
|
| 77 |
+
Display:
|
| 78 |
+
- Tasks completed this session
|
| 79 |
+
- Overall progress: "N/M tasks complete"
|
| 80 |
+
- If all done: suggest archive
|
| 81 |
+
- If paused: explain why and wait for guidance
|
| 82 |
+
|
| 83 |
+
**Output During Implementation**
|
| 84 |
+
|
| 85 |
+
```
|
| 86 |
+
## Implementing: <change-name> (schema: <schema-name>)
|
| 87 |
+
|
| 88 |
+
Working on task 3/7: <task description>
|
| 89 |
+
[...implementation happening...]
|
| 90 |
+
✓ Task complete
|
| 91 |
+
|
| 92 |
+
Working on task 4/7: <task description>
|
| 93 |
+
[...implementation happening...]
|
| 94 |
+
✓ Task complete
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
**Output On Completion**
|
| 98 |
+
|
| 99 |
+
```
|
| 100 |
+
## Implementation Complete
|
| 101 |
+
|
| 102 |
+
**Change:** <change-name>
|
| 103 |
+
**Schema:** <schema-name>
|
| 104 |
+
**Progress:** 7/7 tasks complete ✓
|
| 105 |
+
|
| 106 |
+
### Completed This Session
|
| 107 |
+
- [x] Task 1
|
| 108 |
+
- [x] Task 2
|
| 109 |
+
...
|
| 110 |
+
|
| 111 |
+
All tasks complete! You can archive this change with `/opsx:archive`.
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
**Output On Pause (Issue Encountered)**
|
| 115 |
+
|
| 116 |
+
```
|
| 117 |
+
## Implementation Paused
|
| 118 |
+
|
| 119 |
+
**Change:** <change-name>
|
| 120 |
+
**Schema:** <schema-name>
|
| 121 |
+
**Progress:** 4/7 tasks complete
|
| 122 |
+
|
| 123 |
+
### Issue Encountered
|
| 124 |
+
<description of the issue>
|
| 125 |
+
|
| 126 |
+
**Options:**
|
| 127 |
+
1. <option 1>
|
| 128 |
+
2. <option 2>
|
| 129 |
+
3. Other approach
|
| 130 |
+
|
| 131 |
+
What would you like to do?
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
**Guardrails**
|
| 135 |
+
- Keep going through tasks until done or blocked
|
| 136 |
+
- Always read context files before starting (from the apply instructions output)
|
| 137 |
+
- If task is ambiguous, pause and ask before implementing
|
| 138 |
+
- If implementation reveals issues, pause and suggest artifact updates
|
| 139 |
+
- Keep code changes minimal and scoped to each task
|
| 140 |
+
- Update task checkbox immediately after completing each task
|
| 141 |
+
- Pause on errors, blockers, or unclear requirements - don't guess
|
| 142 |
+
- Use contextFiles from CLI output, don't assume specific file names
|
| 143 |
+
|
| 144 |
+
**Fluid Workflow Integration**
|
| 145 |
+
|
| 146 |
+
This skill supports the "actions on a change" model:
|
| 147 |
+
|
| 148 |
+
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
| 149 |
+
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|
.github/prompts/opsx-archive.prompt.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Archive a completed change in the experimental workflow
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Archive a completed change in the experimental workflow.
|
| 6 |
+
|
| 7 |
+
**Input**: Optionally specify a change name after `/opsx:archive` (e.g., `/opsx:archive add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 8 |
+
|
| 9 |
+
**Steps**
|
| 10 |
+
|
| 11 |
+
1. **If no change name provided, prompt for selection**
|
| 12 |
+
|
| 13 |
+
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
| 14 |
+
|
| 15 |
+
Show only active changes (not already archived).
|
| 16 |
+
Include the schema used for each change if available.
|
| 17 |
+
|
| 18 |
+
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
| 19 |
+
|
| 20 |
+
2. **Check artifact completion status**
|
| 21 |
+
|
| 22 |
+
Run `openspec status --change "<name>" --json` to check artifact completion.
|
| 23 |
+
|
| 24 |
+
Parse the JSON to understand:
|
| 25 |
+
- `schemaName`: The workflow being used
|
| 26 |
+
- `artifacts`: List of artifacts with their status (`done` or other)
|
| 27 |
+
|
| 28 |
+
**If any artifacts are not `done`:**
|
| 29 |
+
- Display warning listing incomplete artifacts
|
| 30 |
+
- Prompt user for confirmation to continue
|
| 31 |
+
- Proceed if user confirms
|
| 32 |
+
|
| 33 |
+
3. **Check task completion status**
|
| 34 |
+
|
| 35 |
+
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
| 36 |
+
|
| 37 |
+
Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).
|
| 38 |
+
|
| 39 |
+
**If incomplete tasks found:**
|
| 40 |
+
- Display warning showing count of incomplete tasks
|
| 41 |
+
- Prompt user for confirmation to continue
|
| 42 |
+
- Proceed if user confirms
|
| 43 |
+
|
| 44 |
+
**If no tasks file exists:** Proceed without task-related warning.
|
| 45 |
+
|
| 46 |
+
4. **Assess delta spec sync state**
|
| 47 |
+
|
| 48 |
+
Check for delta specs at `openspec/changes/<name>/specs/`. If none exist, proceed without sync prompt.
|
| 49 |
+
|
| 50 |
+
**If delta specs exist:**
|
| 51 |
+
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
| 52 |
+
- Determine what changes would be applied (adds, modifications, removals, renames)
|
| 53 |
+
- Show a combined summary before prompting
|
| 54 |
+
|
| 55 |
+
**Prompt options:**
|
| 56 |
+
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
| 57 |
+
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
| 58 |
+
|
| 59 |
+
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
| 60 |
+
|
| 61 |
+
5. **Perform the archive**
|
| 62 |
+
|
| 63 |
+
Create the archive directory if it doesn't exist:
|
| 64 |
+
```bash
|
| 65 |
+
mkdir -p openspec/changes/archive
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
| 69 |
+
|
| 70 |
+
**Check if target already exists:**
|
| 71 |
+
- If yes: Fail with error, suggest renaming existing archive or using different date
|
| 72 |
+
- If no: Move the change directory to archive
|
| 73 |
+
|
| 74 |
+
```bash
|
| 75 |
+
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
6. **Display summary**
|
| 79 |
+
|
| 80 |
+
Show archive completion summary including:
|
| 81 |
+
- Change name
|
| 82 |
+
- Schema that was used
|
| 83 |
+
- Archive location
|
| 84 |
+
- Spec sync status (synced / sync skipped / no delta specs)
|
| 85 |
+
- Note about any warnings (incomplete artifacts/tasks)
|
| 86 |
+
|
| 87 |
+
**Output On Success**
|
| 88 |
+
|
| 89 |
+
```
|
| 90 |
+
## Archive Complete
|
| 91 |
+
|
| 92 |
+
**Change:** <change-name>
|
| 93 |
+
**Schema:** <schema-name>
|
| 94 |
+
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
| 95 |
+
**Specs:** ✓ Synced to main specs
|
| 96 |
+
|
| 97 |
+
All artifacts complete. All tasks complete.
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
**Output On Success (No Delta Specs)**
|
| 101 |
+
|
| 102 |
+
```
|
| 103 |
+
## Archive Complete
|
| 104 |
+
|
| 105 |
+
**Change:** <change-name>
|
| 106 |
+
**Schema:** <schema-name>
|
| 107 |
+
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
| 108 |
+
**Specs:** No delta specs
|
| 109 |
+
|
| 110 |
+
All artifacts complete. All tasks complete.
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
**Output On Success With Warnings**
|
| 114 |
+
|
| 115 |
+
```
|
| 116 |
+
## Archive Complete (with warnings)
|
| 117 |
+
|
| 118 |
+
**Change:** <change-name>
|
| 119 |
+
**Schema:** <schema-name>
|
| 120 |
+
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
| 121 |
+
**Specs:** Sync skipped (user chose to skip)
|
| 122 |
+
|
| 123 |
+
**Warnings:**
|
| 124 |
+
- Archived with 2 incomplete artifacts
|
| 125 |
+
- Archived with 3 incomplete tasks
|
| 126 |
+
- Delta spec sync was skipped (user chose to skip)
|
| 127 |
+
|
| 128 |
+
Review the archive if this was not intentional.
|
| 129 |
+
```
|
| 130 |
+
|
| 131 |
+
**Output On Error (Archive Exists)**
|
| 132 |
+
|
| 133 |
+
```
|
| 134 |
+
## Archive Failed
|
| 135 |
+
|
| 136 |
+
**Change:** <change-name>
|
| 137 |
+
**Target:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
| 138 |
+
|
| 139 |
+
Target archive directory already exists.
|
| 140 |
+
|
| 141 |
+
**Options:**
|
| 142 |
+
1. Rename the existing archive
|
| 143 |
+
2. Delete the existing archive if it's a duplicate
|
| 144 |
+
3. Wait until a different date to archive
|
| 145 |
+
```
|
| 146 |
+
|
| 147 |
+
**Guardrails**
|
| 148 |
+
- Always prompt for change selection if not provided
|
| 149 |
+
- Use artifact graph (openspec status --json) for completion checking
|
| 150 |
+
- Don't block archive on warnings - just inform and confirm
|
| 151 |
+
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
| 152 |
+
- Show clear summary of what happened
|
| 153 |
+
- If sync is requested, use the Skill tool to invoke `openspec-sync-specs` (agent-driven)
|
| 154 |
+
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
.github/prompts/opsx-bulk-archive.prompt.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Archive multiple completed changes at once
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Archive multiple completed changes in a single operation.
|
| 6 |
+
|
| 7 |
+
This skill allows you to batch-archive changes, handling spec conflicts intelligently by checking the codebase to determine what's actually implemented.
|
| 8 |
+
|
| 9 |
+
**Input**: None required (prompts for selection)
|
| 10 |
+
|
| 11 |
+
**Steps**
|
| 12 |
+
|
| 13 |
+
1. **Get active changes**
|
| 14 |
+
|
| 15 |
+
Run `openspec list --json` to get all active changes.
|
| 16 |
+
|
| 17 |
+
If no active changes exist, inform user and stop.
|
| 18 |
+
|
| 19 |
+
2. **Prompt for change selection**
|
| 20 |
+
|
| 21 |
+
Use **AskUserQuestion tool** with multi-select to let user choose changes:
|
| 22 |
+
- Show each change with its schema
|
| 23 |
+
- Include an option for "All changes"
|
| 24 |
+
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
| 25 |
+
|
| 26 |
+
**IMPORTANT**: Do NOT auto-select. Always let the user choose.
|
| 27 |
+
|
| 28 |
+
3. **Batch validation - gather status for all selected changes**
|
| 29 |
+
|
| 30 |
+
For each selected change, collect:
|
| 31 |
+
|
| 32 |
+
a. **Artifact status** - Run `openspec status --change "<name>" --json`
|
| 33 |
+
- Parse `schemaName` and `artifacts` list
|
| 34 |
+
- Note which artifacts are `done` vs other states
|
| 35 |
+
|
| 36 |
+
b. **Task completion** - Read `openspec/changes/<name>/tasks.md`
|
| 37 |
+
- Count `- [ ]` (incomplete) vs `- [x]` (complete)
|
| 38 |
+
- If no tasks file exists, note as "No tasks"
|
| 39 |
+
|
| 40 |
+
c. **Delta specs** - Check `openspec/changes/<name>/specs/` directory
|
| 41 |
+
- List which capability specs exist
|
| 42 |
+
- For each, extract requirement names (lines matching `### Requirement: <name>`)
|
| 43 |
+
|
| 44 |
+
4. **Detect spec conflicts**
|
| 45 |
+
|
| 46 |
+
Build a map of `capability -> [changes that touch it]`:
|
| 47 |
+
|
| 48 |
+
```
|
| 49 |
+
auth -> [change-a, change-b] <- CONFLICT (2+ changes)
|
| 50 |
+
api -> [change-c] <- OK (only 1 change)
|
| 51 |
+
```
|
| 52 |
+
|
| 53 |
+
A conflict exists when 2+ selected changes have delta specs for the same capability.
|
| 54 |
+
|
| 55 |
+
5. **Resolve conflicts agentically**
|
| 56 |
+
|
| 57 |
+
**For each conflict**, investigate the codebase:
|
| 58 |
+
|
| 59 |
+
a. **Read the delta specs** from each conflicting change to understand what each claims to add/modify
|
| 60 |
+
|
| 61 |
+
b. **Search the codebase** for implementation evidence:
|
| 62 |
+
- Look for code implementing requirements from each delta spec
|
| 63 |
+
- Check for related files, functions, or tests
|
| 64 |
+
|
| 65 |
+
c. **Determine resolution**:
|
| 66 |
+
- If only one change is actually implemented -> sync that one's specs
|
| 67 |
+
- If both implemented -> apply in chronological order (older first, newer overwrites)
|
| 68 |
+
- If neither implemented -> skip spec sync, warn user
|
| 69 |
+
|
| 70 |
+
d. **Record resolution** for each conflict:
|
| 71 |
+
- Which change's specs to apply
|
| 72 |
+
- In what order (if both)
|
| 73 |
+
- Rationale (what was found in codebase)
|
| 74 |
+
|
| 75 |
+
6. **Show consolidated status table**
|
| 76 |
+
|
| 77 |
+
Display a table summarizing all changes:
|
| 78 |
+
|
| 79 |
+
```
|
| 80 |
+
| Change | Artifacts | Tasks | Specs | Conflicts | Status |
|
| 81 |
+
|---------------------|-----------|-------|---------|-----------|--------|
|
| 82 |
+
| schema-management | Done | 5/5 | 2 delta | None | Ready |
|
| 83 |
+
| project-config | Done | 3/3 | 1 delta | None | Ready |
|
| 84 |
+
| add-oauth | Done | 4/4 | 1 delta | auth (!) | Ready* |
|
| 85 |
+
| add-verify-skill | 1 left | 2/5 | None | None | Warn |
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
For conflicts, show the resolution:
|
| 89 |
+
```
|
| 90 |
+
* Conflict resolution:
|
| 91 |
+
- auth spec: Will apply add-oauth then add-jwt (both implemented, chronological order)
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
For incomplete changes, show warnings:
|
| 95 |
+
```
|
| 96 |
+
Warnings:
|
| 97 |
+
- add-verify-skill: 1 incomplete artifact, 3 incomplete tasks
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
7. **Confirm batch operation**
|
| 101 |
+
|
| 102 |
+
Use **AskUserQuestion tool** with a single confirmation:
|
| 103 |
+
|
| 104 |
+
- "Archive N changes?" with options based on status
|
| 105 |
+
- Options might include:
|
| 106 |
+
- "Archive all N changes"
|
| 107 |
+
- "Archive only N ready changes (skip incomplete)"
|
| 108 |
+
- "Cancel"
|
| 109 |
+
|
| 110 |
+
If there are incomplete changes, make clear they'll be archived with warnings.
|
| 111 |
+
|
| 112 |
+
8. **Execute archive for each confirmed change**
|
| 113 |
+
|
| 114 |
+
Process changes in the determined order (respecting conflict resolution):
|
| 115 |
+
|
| 116 |
+
a. **Sync specs** if delta specs exist:
|
| 117 |
+
- Use the openspec-sync-specs approach (agent-driven intelligent merge)
|
| 118 |
+
- For conflicts, apply in resolved order
|
| 119 |
+
- Track if sync was done
|
| 120 |
+
|
| 121 |
+
b. **Perform the archive**:
|
| 122 |
+
```bash
|
| 123 |
+
mkdir -p openspec/changes/archive
|
| 124 |
+
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
c. **Track outcome** for each change:
|
| 128 |
+
- Success: archived successfully
|
| 129 |
+
- Failed: error during archive (record error)
|
| 130 |
+
- Skipped: user chose not to archive (if applicable)
|
| 131 |
+
|
| 132 |
+
9. **Display summary**
|
| 133 |
+
|
| 134 |
+
Show final results:
|
| 135 |
+
|
| 136 |
+
```
|
| 137 |
+
## Bulk Archive Complete
|
| 138 |
+
|
| 139 |
+
Archived 3 changes:
|
| 140 |
+
- schema-management-cli -> archive/2026-01-19-schema-management-cli/
|
| 141 |
+
- project-config -> archive/2026-01-19-project-config/
|
| 142 |
+
- add-oauth -> archive/2026-01-19-add-oauth/
|
| 143 |
+
|
| 144 |
+
Skipped 1 change:
|
| 145 |
+
- add-verify-skill (user chose not to archive incomplete)
|
| 146 |
+
|
| 147 |
+
Spec sync summary:
|
| 148 |
+
- 4 delta specs synced to main specs
|
| 149 |
+
- 1 conflict resolved (auth: applied both in chronological order)
|
| 150 |
+
```
|
| 151 |
+
|
| 152 |
+
If any failures:
|
| 153 |
+
```
|
| 154 |
+
Failed 1 change:
|
| 155 |
+
- some-change: Archive directory already exists
|
| 156 |
+
```
|
| 157 |
+
|
| 158 |
+
**Conflict Resolution Examples**
|
| 159 |
+
|
| 160 |
+
Example 1: Only one implemented
|
| 161 |
+
```
|
| 162 |
+
Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt]
|
| 163 |
+
|
| 164 |
+
Checking add-oauth:
|
| 165 |
+
- Delta adds "OAuth Provider Integration" requirement
|
| 166 |
+
- Searching codebase... found src/auth/oauth.ts implementing OAuth flow
|
| 167 |
+
|
| 168 |
+
Checking add-jwt:
|
| 169 |
+
- Delta adds "JWT Token Handling" requirement
|
| 170 |
+
- Searching codebase... no JWT implementation found
|
| 171 |
+
|
| 172 |
+
Resolution: Only add-oauth is implemented. Will sync add-oauth specs only.
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
Example 2: Both implemented
|
| 176 |
+
```
|
| 177 |
+
Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql]
|
| 178 |
+
|
| 179 |
+
Checking add-rest-api (created 2026-01-10):
|
| 180 |
+
- Delta adds "REST Endpoints" requirement
|
| 181 |
+
- Searching codebase... found src/api/rest.ts
|
| 182 |
+
|
| 183 |
+
Checking add-graphql (created 2026-01-15):
|
| 184 |
+
- Delta adds "GraphQL Schema" requirement
|
| 185 |
+
- Searching codebase... found src/api/graphql.ts
|
| 186 |
+
|
| 187 |
+
Resolution: Both implemented. Will apply add-rest-api specs first,
|
| 188 |
+
then add-graphql specs (chronological order, newer takes precedence).
|
| 189 |
+
```
|
| 190 |
+
|
| 191 |
+
**Output On Success**
|
| 192 |
+
|
| 193 |
+
```
|
| 194 |
+
## Bulk Archive Complete
|
| 195 |
+
|
| 196 |
+
Archived N changes:
|
| 197 |
+
- <change-1> -> archive/YYYY-MM-DD-<change-1>/
|
| 198 |
+
- <change-2> -> archive/YYYY-MM-DD-<change-2>/
|
| 199 |
+
|
| 200 |
+
Spec sync summary:
|
| 201 |
+
- N delta specs synced to main specs
|
| 202 |
+
- No conflicts (or: M conflicts resolved)
|
| 203 |
+
```
|
| 204 |
+
|
| 205 |
+
**Output On Partial Success**
|
| 206 |
+
|
| 207 |
+
```
|
| 208 |
+
## Bulk Archive Complete (partial)
|
| 209 |
+
|
| 210 |
+
Archived N changes:
|
| 211 |
+
- <change-1> -> archive/YYYY-MM-DD-<change-1>/
|
| 212 |
+
|
| 213 |
+
Skipped M changes:
|
| 214 |
+
- <change-2> (user chose not to archive incomplete)
|
| 215 |
+
|
| 216 |
+
Failed K changes:
|
| 217 |
+
- <change-3>: Archive directory already exists
|
| 218 |
+
```
|
| 219 |
+
|
| 220 |
+
**Output When No Changes**
|
| 221 |
+
|
| 222 |
+
```
|
| 223 |
+
## No Changes to Archive
|
| 224 |
+
|
| 225 |
+
No active changes found. Create a new change to get started.
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
**Guardrails**
|
| 229 |
+
- Allow any number of changes (1+ is fine, 2+ is the typical use case)
|
| 230 |
+
- Always prompt for selection, never auto-select
|
| 231 |
+
- Detect spec conflicts early and resolve by checking codebase
|
| 232 |
+
- When both changes are implemented, apply specs in chronological order
|
| 233 |
+
- Skip spec sync only when implementation is missing (warn user)
|
| 234 |
+
- Show clear per-change status before confirming
|
| 235 |
+
- Use single confirmation for entire batch
|
| 236 |
+
- Track and report all outcomes (success/skip/fail)
|
| 237 |
+
- Preserve .openspec.yaml when moving to archive
|
| 238 |
+
- Archive directory target uses current date: YYYY-MM-DD-<name>
|
| 239 |
+
- If archive target exists, fail that change but continue with others
|
.github/prompts/opsx-continue.prompt.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Continue working on a change - create the next artifact (Experimental)
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Continue working on a change by creating the next artifact.
|
| 6 |
+
|
| 7 |
+
**Input**: Optionally specify a change name after `/opsx:continue` (e.g., `/opsx:continue add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 8 |
+
|
| 9 |
+
**Steps**
|
| 10 |
+
|
| 11 |
+
1. **If no change name provided, prompt for selection**
|
| 12 |
+
|
| 13 |
+
Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to work on.
|
| 14 |
+
|
| 15 |
+
Present the top 3-4 most recently modified changes as options, showing:
|
| 16 |
+
- Change name
|
| 17 |
+
- Schema (from `schema` field if present, otherwise "spec-driven")
|
| 18 |
+
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
| 19 |
+
- How recently it was modified (from `lastModified` field)
|
| 20 |
+
|
| 21 |
+
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to continue.
|
| 22 |
+
|
| 23 |
+
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
| 24 |
+
|
| 25 |
+
2. **Check current status**
|
| 26 |
+
```bash
|
| 27 |
+
openspec status --change "<name>" --json
|
| 28 |
+
```
|
| 29 |
+
Parse the JSON to understand current state. The response includes:
|
| 30 |
+
- `schemaName`: The workflow schema being used (e.g., "spec-driven")
|
| 31 |
+
- `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
|
| 32 |
+
- `isComplete`: Boolean indicating if all artifacts are complete
|
| 33 |
+
|
| 34 |
+
3. **Act based on status**:
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
**If all artifacts are complete (`isComplete: true`)**:
|
| 39 |
+
- Congratulate the user
|
| 40 |
+
- Show final status including the schema used
|
| 41 |
+
- Suggest: "All artifacts created! You can now implement this change with `/opsx:apply` or archive it with `/opsx:archive`."
|
| 42 |
+
- STOP
|
| 43 |
+
|
| 44 |
+
---
|
| 45 |
+
|
| 46 |
+
**If artifacts are ready to create** (status shows artifacts with `status: "ready"`):
|
| 47 |
+
- Pick the FIRST artifact with `status: "ready"` from the status output
|
| 48 |
+
- Get its instructions:
|
| 49 |
+
```bash
|
| 50 |
+
openspec instructions <artifact-id> --change "<name>" --json
|
| 51 |
+
```
|
| 52 |
+
- Parse the JSON. The key fields are:
|
| 53 |
+
- `context`: Project background (constraints for you - do NOT include in output)
|
| 54 |
+
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
| 55 |
+
- `template`: The structure to use for your output file
|
| 56 |
+
- `instruction`: Schema-specific guidance
|
| 57 |
+
- `outputPath`: Where to write the artifact
|
| 58 |
+
- `dependencies`: Completed artifacts to read for context
|
| 59 |
+
- **Create the artifact file**:
|
| 60 |
+
- Read any completed dependency files for context
|
| 61 |
+
- Use `template` as the structure - fill in its sections
|
| 62 |
+
- Apply `context` and `rules` as constraints when writing - but do NOT copy them into the file
|
| 63 |
+
- Write to the output path specified in instructions
|
| 64 |
+
- Show what was created and what's now unlocked
|
| 65 |
+
- STOP after creating ONE artifact
|
| 66 |
+
|
| 67 |
+
---
|
| 68 |
+
|
| 69 |
+
**If no artifacts are ready (all blocked)**:
|
| 70 |
+
- This shouldn't happen with a valid schema
|
| 71 |
+
- Show status and suggest checking for issues
|
| 72 |
+
|
| 73 |
+
4. **After creating an artifact, show progress**
|
| 74 |
+
```bash
|
| 75 |
+
openspec status --change "<name>"
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
**Output**
|
| 79 |
+
|
| 80 |
+
After each invocation, show:
|
| 81 |
+
- Which artifact was created
|
| 82 |
+
- Schema workflow being used
|
| 83 |
+
- Current progress (N/M complete)
|
| 84 |
+
- What artifacts are now unlocked
|
| 85 |
+
- Prompt: "Run `/opsx:continue` to create the next artifact"
|
| 86 |
+
|
| 87 |
+
**Artifact Creation Guidelines**
|
| 88 |
+
|
| 89 |
+
The artifact types and their purpose depend on the schema. Use the `instruction` field from the instructions output to understand what to create.
|
| 90 |
+
|
| 91 |
+
Common artifact patterns:
|
| 92 |
+
|
| 93 |
+
**spec-driven schema** (proposal → specs → design → tasks):
|
| 94 |
+
- **proposal.md**: Ask user about the change if not clear. Fill in Why, What Changes, Capabilities, Impact.
|
| 95 |
+
- The Capabilities section is critical - each capability listed will need a spec file.
|
| 96 |
+
- **specs/<capability>/spec.md**: Create one spec per capability listed in the proposal's Capabilities section (use the capability name, not the change name).
|
| 97 |
+
- **design.md**: Document technical decisions, architecture, and implementation approach.
|
| 98 |
+
- **tasks.md**: Break down implementation into checkboxed tasks.
|
| 99 |
+
|
| 100 |
+
For other schemas, follow the `instruction` field from the CLI output.
|
| 101 |
+
|
| 102 |
+
**Guardrails**
|
| 103 |
+
- Create ONE artifact per invocation
|
| 104 |
+
- Always read dependency artifacts before creating a new one
|
| 105 |
+
- Never skip artifacts or create out of order
|
| 106 |
+
- If context is unclear, ask the user before creating
|
| 107 |
+
- Verify the artifact file exists after writing before marking progress
|
| 108 |
+
- Use the schema's artifact sequence, don't assume specific artifact names
|
| 109 |
+
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
| 110 |
+
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
| 111 |
+
- These guide what you write, but should never appear in the output
|
.github/prompts/opsx-explore.prompt.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Enter explore mode - think through ideas, investigate problems, clarify requirements
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
| 6 |
+
|
| 7 |
+
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
| 8 |
+
|
| 9 |
+
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
| 10 |
+
|
| 11 |
+
**Input**: The argument after `/opsx:explore` is whatever the user wants to think about. Could be:
|
| 12 |
+
- A vague idea: "real-time collaboration"
|
| 13 |
+
- A specific problem: "the auth system is getting unwieldy"
|
| 14 |
+
- A change name: "add-dark-mode" (to explore in context of that change)
|
| 15 |
+
- A comparison: "postgres vs sqlite for this"
|
| 16 |
+
- Nothing (just enter explore mode)
|
| 17 |
+
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
## The Stance
|
| 21 |
+
|
| 22 |
+
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
| 23 |
+
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
| 24 |
+
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
| 25 |
+
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
| 26 |
+
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
| 27 |
+
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
## What You Might Do
|
| 32 |
+
|
| 33 |
+
Depending on what the user brings, you might:
|
| 34 |
+
|
| 35 |
+
**Explore the problem space**
|
| 36 |
+
- Ask clarifying questions that emerge from what they said
|
| 37 |
+
- Challenge assumptions
|
| 38 |
+
- Reframe the problem
|
| 39 |
+
- Find analogies
|
| 40 |
+
|
| 41 |
+
**Investigate the codebase**
|
| 42 |
+
- Map existing architecture relevant to the discussion
|
| 43 |
+
- Find integration points
|
| 44 |
+
- Identify patterns already in use
|
| 45 |
+
- Surface hidden complexity
|
| 46 |
+
|
| 47 |
+
**Compare options**
|
| 48 |
+
- Brainstorm multiple approaches
|
| 49 |
+
- Build comparison tables
|
| 50 |
+
- Sketch tradeoffs
|
| 51 |
+
- Recommend a path (if asked)
|
| 52 |
+
|
| 53 |
+
**Visualize**
|
| 54 |
+
```
|
| 55 |
+
┌─────────────────────────────────────────┐
|
| 56 |
+
│ Use ASCII diagrams liberally │
|
| 57 |
+
├─────────────────────────────────────────┤
|
| 58 |
+
│ │
|
| 59 |
+
│ ┌────────┐ ┌────────┐ │
|
| 60 |
+
│ │ State │────────▶│ State │ │
|
| 61 |
+
│ │ A │ │ B │ │
|
| 62 |
+
│ └────────┘ └────────┘ │
|
| 63 |
+
│ │
|
| 64 |
+
│ System diagrams, state machines, │
|
| 65 |
+
│ data flows, architecture sketches, │
|
| 66 |
+
│ dependency graphs, comparison tables │
|
| 67 |
+
│ │
|
| 68 |
+
└─────────────────────────────────────────┘
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
**Surface risks and unknowns**
|
| 72 |
+
- Identify what could go wrong
|
| 73 |
+
- Find gaps in understanding
|
| 74 |
+
- Suggest spikes or investigations
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
+
## OpenSpec Awareness
|
| 79 |
+
|
| 80 |
+
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
| 81 |
+
|
| 82 |
+
### Check for context
|
| 83 |
+
|
| 84 |
+
At the start, quickly check what exists:
|
| 85 |
+
```bash
|
| 86 |
+
openspec list --json
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
This tells you:
|
| 90 |
+
- If there are active changes
|
| 91 |
+
- Their names, schemas, and status
|
| 92 |
+
- What the user might be working on
|
| 93 |
+
|
| 94 |
+
If the user mentioned a specific change name, read its artifacts for context.
|
| 95 |
+
|
| 96 |
+
### When no change exists
|
| 97 |
+
|
| 98 |
+
Think freely. When insights crystallize, you might offer:
|
| 99 |
+
|
| 100 |
+
- "This feels solid enough to start a change. Want me to create a proposal?"
|
| 101 |
+
- Or keep exploring - no pressure to formalize
|
| 102 |
+
|
| 103 |
+
### When a change exists
|
| 104 |
+
|
| 105 |
+
If the user mentions a change or you detect one is relevant:
|
| 106 |
+
|
| 107 |
+
1. **Read existing artifacts for context**
|
| 108 |
+
- `openspec/changes/<name>/proposal.md`
|
| 109 |
+
- `openspec/changes/<name>/design.md`
|
| 110 |
+
- `openspec/changes/<name>/tasks.md`
|
| 111 |
+
- etc.
|
| 112 |
+
|
| 113 |
+
2. **Reference them naturally in conversation**
|
| 114 |
+
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
| 115 |
+
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
| 116 |
+
|
| 117 |
+
3. **Offer to capture when decisions are made**
|
| 118 |
+
|
| 119 |
+
| Insight Type | Where to Capture |
|
| 120 |
+
|--------------|------------------|
|
| 121 |
+
| New requirement discovered | `specs/<capability>/spec.md` |
|
| 122 |
+
| Requirement changed | `specs/<capability>/spec.md` |
|
| 123 |
+
| Design decision made | `design.md` |
|
| 124 |
+
| Scope changed | `proposal.md` |
|
| 125 |
+
| New work identified | `tasks.md` |
|
| 126 |
+
| Assumption invalidated | Relevant artifact |
|
| 127 |
+
|
| 128 |
+
Example offers:
|
| 129 |
+
- "That's a design decision. Capture it in design.md?"
|
| 130 |
+
- "This is a new requirement. Add it to specs?"
|
| 131 |
+
- "This changes scope. Update the proposal?"
|
| 132 |
+
|
| 133 |
+
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
| 134 |
+
|
| 135 |
+
---
|
| 136 |
+
|
| 137 |
+
## What You Don't Have To Do
|
| 138 |
+
|
| 139 |
+
- Follow a script
|
| 140 |
+
- Ask the same questions every time
|
| 141 |
+
- Produce a specific artifact
|
| 142 |
+
- Reach a conclusion
|
| 143 |
+
- Stay on topic if a tangent is valuable
|
| 144 |
+
- Be brief (this is thinking time)
|
| 145 |
+
|
| 146 |
+
---
|
| 147 |
+
|
| 148 |
+
## Ending Discovery
|
| 149 |
+
|
| 150 |
+
There's no required ending. Discovery might:
|
| 151 |
+
|
| 152 |
+
- **Flow into a proposal**: "Ready to start? I can create a change proposal."
|
| 153 |
+
- **Result in artifact updates**: "Updated design.md with these decisions"
|
| 154 |
+
- **Just provide clarity**: User has what they need, moves on
|
| 155 |
+
- **Continue later**: "We can pick this up anytime"
|
| 156 |
+
|
| 157 |
+
When things crystallize, you might offer a summary - but it's optional. Sometimes the thinking IS the value.
|
| 158 |
+
|
| 159 |
+
---
|
| 160 |
+
|
| 161 |
+
## Guardrails
|
| 162 |
+
|
| 163 |
+
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
|
| 164 |
+
- **Don't fake understanding** - If something is unclear, dig deeper
|
| 165 |
+
- **Don't rush** - Discovery is thinking time, not task time
|
| 166 |
+
- **Don't force structure** - Let patterns emerge naturally
|
| 167 |
+
- **Don't auto-capture** - Offer to save insights, don't just do it
|
| 168 |
+
- **Do visualize** - A good diagram is worth many paragraphs
|
| 169 |
+
- **Do explore the codebase** - Ground discussions in reality
|
| 170 |
+
- **Do question assumptions** - Including the user's and your own
|
.github/prompts/opsx-ff.prompt.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Create a change and generate all artifacts needed for implementation in one go
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Fast-forward through artifact creation - generate everything needed to start implementation.
|
| 6 |
+
|
| 7 |
+
**Input**: The argument after `/opsx:ff` is the change name (kebab-case), OR a description of what the user wants to build.
|
| 8 |
+
|
| 9 |
+
**Steps**
|
| 10 |
+
|
| 11 |
+
1. **If no input provided, ask what they want to build**
|
| 12 |
+
|
| 13 |
+
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
| 14 |
+
> "What change do you want to work on? Describe what you want to build or fix."
|
| 15 |
+
|
| 16 |
+
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
| 17 |
+
|
| 18 |
+
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
| 19 |
+
|
| 20 |
+
2. **Create the change directory**
|
| 21 |
+
```bash
|
| 22 |
+
openspec new change "<name>"
|
| 23 |
+
```
|
| 24 |
+
This creates a scaffolded change at `openspec/changes/<name>/`.
|
| 25 |
+
|
| 26 |
+
3. **Get the artifact build order**
|
| 27 |
+
```bash
|
| 28 |
+
openspec status --change "<name>" --json
|
| 29 |
+
```
|
| 30 |
+
Parse the JSON to get:
|
| 31 |
+
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
| 32 |
+
- `artifacts`: list of all artifacts with their status and dependencies
|
| 33 |
+
|
| 34 |
+
4. **Create artifacts in sequence until apply-ready**
|
| 35 |
+
|
| 36 |
+
Use the **TodoWrite tool** to track progress through the artifacts.
|
| 37 |
+
|
| 38 |
+
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
| 39 |
+
|
| 40 |
+
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
| 41 |
+
- Get instructions:
|
| 42 |
+
```bash
|
| 43 |
+
openspec instructions <artifact-id> --change "<name>" --json
|
| 44 |
+
```
|
| 45 |
+
- The instructions JSON includes:
|
| 46 |
+
- `context`: Project background (constraints for you - do NOT include in output)
|
| 47 |
+
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
| 48 |
+
- `template`: The structure to use for your output file
|
| 49 |
+
- `instruction`: Schema-specific guidance for this artifact type
|
| 50 |
+
- `outputPath`: Where to write the artifact
|
| 51 |
+
- `dependencies`: Completed artifacts to read for context
|
| 52 |
+
- Read any completed dependency files for context
|
| 53 |
+
- Create the artifact file using `template` as the structure
|
| 54 |
+
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
| 55 |
+
- Show brief progress: "✓ Created <artifact-id>"
|
| 56 |
+
|
| 57 |
+
b. **Continue until all `applyRequires` artifacts are complete**
|
| 58 |
+
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
| 59 |
+
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
| 60 |
+
- Stop when all `applyRequires` artifacts are done
|
| 61 |
+
|
| 62 |
+
c. **If an artifact requires user input** (unclear context):
|
| 63 |
+
- Use **AskUserQuestion tool** to clarify
|
| 64 |
+
- Then continue with creation
|
| 65 |
+
|
| 66 |
+
5. **Show final status**
|
| 67 |
+
```bash
|
| 68 |
+
openspec status --change "<name>"
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
**Output**
|
| 72 |
+
|
| 73 |
+
After completing all artifacts, summarize:
|
| 74 |
+
- Change name and location
|
| 75 |
+
- List of artifacts created with brief descriptions
|
| 76 |
+
- What's ready: "All artifacts created! Ready for implementation."
|
| 77 |
+
- Prompt: "Run `/opsx:apply` to start implementing."
|
| 78 |
+
|
| 79 |
+
**Artifact Creation Guidelines**
|
| 80 |
+
|
| 81 |
+
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
| 82 |
+
- The schema defines what each artifact should contain - follow it
|
| 83 |
+
- Read dependency artifacts for context before creating new ones
|
| 84 |
+
- Use `template` as the structure for your output file - fill in its sections
|
| 85 |
+
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
| 86 |
+
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
| 87 |
+
- These guide what you write, but should never appear in the output
|
| 88 |
+
|
| 89 |
+
**Guardrails**
|
| 90 |
+
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
| 91 |
+
- Always read dependency artifacts before creating a new one
|
| 92 |
+
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
| 93 |
+
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
| 94 |
+
- Verify each artifact file exists after writing before proceeding to next
|
.github/prompts/opsx-new.prompt.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Start a new change using the experimental artifact workflow (OPSX)
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Start a new change using the experimental artifact-driven approach.
|
| 6 |
+
|
| 7 |
+
**Input**: The argument after `/opsx:new` is the change name (kebab-case), OR a description of what the user wants to build.
|
| 8 |
+
|
| 9 |
+
**Steps**
|
| 10 |
+
|
| 11 |
+
1. **If no input provided, ask what they want to build**
|
| 12 |
+
|
| 13 |
+
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
| 14 |
+
> "What change do you want to work on? Describe what you want to build or fix."
|
| 15 |
+
|
| 16 |
+
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
| 17 |
+
|
| 18 |
+
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
| 19 |
+
|
| 20 |
+
2. **Determine the workflow schema**
|
| 21 |
+
|
| 22 |
+
Use the default schema (omit `--schema`) unless the user explicitly requests a different workflow.
|
| 23 |
+
|
| 24 |
+
**Use a different schema only if the user mentions:**
|
| 25 |
+
- A specific schema name → use `--schema <name>`
|
| 26 |
+
- "show workflows" or "what workflows" → run `openspec schemas --json` and let them choose
|
| 27 |
+
|
| 28 |
+
**Otherwise**: Omit `--schema` to use the default.
|
| 29 |
+
|
| 30 |
+
3. **Create the change directory**
|
| 31 |
+
```bash
|
| 32 |
+
openspec new change "<name>"
|
| 33 |
+
```
|
| 34 |
+
Add `--schema <name>` only if the user requested a specific workflow.
|
| 35 |
+
This creates a scaffolded change at `openspec/changes/<name>/` with the selected schema.
|
| 36 |
+
|
| 37 |
+
4. **Show the artifact status**
|
| 38 |
+
```bash
|
| 39 |
+
openspec status --change "<name>"
|
| 40 |
+
```
|
| 41 |
+
This shows which artifacts need to be created and which are ready (dependencies satisfied).
|
| 42 |
+
|
| 43 |
+
5. **Get instructions for the first artifact**
|
| 44 |
+
The first artifact depends on the schema. Check the status output to find the first artifact with status "ready".
|
| 45 |
+
```bash
|
| 46 |
+
openspec instructions <first-artifact-id> --change "<name>"
|
| 47 |
+
```
|
| 48 |
+
This outputs the template and context for creating the first artifact.
|
| 49 |
+
|
| 50 |
+
6. **STOP and wait for user direction**
|
| 51 |
+
|
| 52 |
+
**Output**
|
| 53 |
+
|
| 54 |
+
After completing the steps, summarize:
|
| 55 |
+
- Change name and location
|
| 56 |
+
- Schema/workflow being used and its artifact sequence
|
| 57 |
+
- Current status (0/N artifacts complete)
|
| 58 |
+
- The template for the first artifact
|
| 59 |
+
- Prompt: "Ready to create the first artifact? Run `/opsx:continue` or just describe what this change is about and I'll draft it."
|
| 60 |
+
|
| 61 |
+
**Guardrails**
|
| 62 |
+
- Do NOT create any artifacts yet - just show the instructions
|
| 63 |
+
- Do NOT advance beyond showing the first artifact template
|
| 64 |
+
- If the name is invalid (not kebab-case), ask for a valid name
|
| 65 |
+
- If a change with that name already exists, suggest using `/opsx:continue` instead
|
| 66 |
+
- Pass --schema if using a non-default workflow
|
.github/prompts/opsx-onboard.prompt.md
ADDED
|
@@ -0,0 +1,547 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Guided onboarding - walk through a complete OpenSpec workflow cycle with narration
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Guide the user through their first complete OpenSpec workflow cycle. This is a teaching experience—you'll do real work in their codebase while explaining each step.
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## Preflight
|
| 10 |
+
|
| 11 |
+
Before starting, check if the OpenSpec CLI is installed:
|
| 12 |
+
|
| 13 |
+
```bash
|
| 14 |
+
# Unix/macOS
|
| 15 |
+
openspec --version 2>&1 || echo "CLI_NOT_INSTALLED"
|
| 16 |
+
# Windows (PowerShell)
|
| 17 |
+
# if (Get-Command openspec -ErrorAction SilentlyContinue) { openspec --version } else { echo "CLI_NOT_INSTALLED" }
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
**If CLI not installed:**
|
| 21 |
+
> OpenSpec CLI is not installed. Install it first, then come back to `/opsx:onboard`.
|
| 22 |
+
|
| 23 |
+
Stop here if not installed.
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
## Phase 1: Welcome
|
| 28 |
+
|
| 29 |
+
Display:
|
| 30 |
+
|
| 31 |
+
```
|
| 32 |
+
## Welcome to OpenSpec!
|
| 33 |
+
|
| 34 |
+
I'll walk you through a complete change cycle—from idea to implementation—using a real task in your codebase. Along the way, you'll learn the workflow by doing it.
|
| 35 |
+
|
| 36 |
+
**What we'll do:**
|
| 37 |
+
1. Pick a small, real task in your codebase
|
| 38 |
+
2. Explore the problem briefly
|
| 39 |
+
3. Create a change (the container for our work)
|
| 40 |
+
4. Build the artifacts: proposal → specs → design → tasks
|
| 41 |
+
5. Implement the tasks
|
| 42 |
+
6. Archive the completed change
|
| 43 |
+
|
| 44 |
+
**Time:** ~15-20 minutes
|
| 45 |
+
|
| 46 |
+
Let's start by finding something to work on.
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
---
|
| 50 |
+
|
| 51 |
+
## Phase 2: Task Selection
|
| 52 |
+
|
| 53 |
+
### Codebase Analysis
|
| 54 |
+
|
| 55 |
+
Scan the codebase for small improvement opportunities. Look for:
|
| 56 |
+
|
| 57 |
+
1. **TODO/FIXME comments** - Search for `TODO`, `FIXME`, `HACK`, `XXX` in code files
|
| 58 |
+
2. **Missing error handling** - `catch` blocks that swallow errors, risky operations without try-catch
|
| 59 |
+
3. **Functions without tests** - Cross-reference `src/` with test directories
|
| 60 |
+
4. **Type issues** - `any` types in TypeScript files (`: any`, `as any`)
|
| 61 |
+
5. **Debug artifacts** - `console.log`, `console.debug`, `debugger` statements in non-debug code
|
| 62 |
+
6. **Missing validation** - User input handlers without validation
|
| 63 |
+
|
| 64 |
+
Also check recent git activity:
|
| 65 |
+
```bash
|
| 66 |
+
# Unix/macOS
|
| 67 |
+
git log --oneline -10 2>/dev/null || echo "No git history"
|
| 68 |
+
# Windows (PowerShell)
|
| 69 |
+
# git log --oneline -10 2>$null; if ($LASTEXITCODE -ne 0) { echo "No git history" }
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
### Present Suggestions
|
| 73 |
+
|
| 74 |
+
From your analysis, present 3-4 specific suggestions:
|
| 75 |
+
|
| 76 |
+
```
|
| 77 |
+
## Task Suggestions
|
| 78 |
+
|
| 79 |
+
Based on scanning your codebase, here are some good starter tasks:
|
| 80 |
+
|
| 81 |
+
**1. [Most promising task]**
|
| 82 |
+
Location: `src/path/to/file.ts:42`
|
| 83 |
+
Scope: ~1-2 files, ~20-30 lines
|
| 84 |
+
Why it's good: [brief reason]
|
| 85 |
+
|
| 86 |
+
**2. [Second task]**
|
| 87 |
+
Location: `src/another/file.ts`
|
| 88 |
+
Scope: ~1 file, ~15 lines
|
| 89 |
+
Why it's good: [brief reason]
|
| 90 |
+
|
| 91 |
+
**3. [Third task]**
|
| 92 |
+
Location: [location]
|
| 93 |
+
Scope: [estimate]
|
| 94 |
+
Why it's good: [brief reason]
|
| 95 |
+
|
| 96 |
+
**4. Something else?**
|
| 97 |
+
Tell me what you'd like to work on.
|
| 98 |
+
|
| 99 |
+
Which task interests you? (Pick a number or describe your own)
|
| 100 |
+
```
|
| 101 |
+
|
| 102 |
+
**If nothing found:** Fall back to asking what the user wants to build:
|
| 103 |
+
> I didn't find obvious quick wins in your codebase. What's something small you've been meaning to add or fix?
|
| 104 |
+
|
| 105 |
+
### Scope Guardrail
|
| 106 |
+
|
| 107 |
+
If the user picks or describes something too large (major feature, multi-day work):
|
| 108 |
+
|
| 109 |
+
```
|
| 110 |
+
That's a valuable task, but it's probably larger than ideal for your first OpenSpec run-through.
|
| 111 |
+
|
| 112 |
+
For learning the workflow, smaller is better—it lets you see the full cycle without getting stuck in implementation details.
|
| 113 |
+
|
| 114 |
+
**Options:**
|
| 115 |
+
1. **Slice it smaller** - What's the smallest useful piece of [their task]? Maybe just [specific slice]?
|
| 116 |
+
2. **Pick something else** - One of the other suggestions, or a different small task?
|
| 117 |
+
3. **Do it anyway** - If you really want to tackle this, we can. Just know it'll take longer.
|
| 118 |
+
|
| 119 |
+
What would you prefer?
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
Let the user override if they insist—this is a soft guardrail.
|
| 123 |
+
|
| 124 |
+
---
|
| 125 |
+
|
| 126 |
+
## Phase 3: Explore Demo
|
| 127 |
+
|
| 128 |
+
Once a task is selected, briefly demonstrate explore mode:
|
| 129 |
+
|
| 130 |
+
```
|
| 131 |
+
Before we create a change, let me quickly show you **explore mode**—it's how you think through problems before committing to a direction.
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
Spend 1-2 minutes investigating the relevant code:
|
| 135 |
+
- Read the file(s) involved
|
| 136 |
+
- Draw a quick ASCII diagram if it helps
|
| 137 |
+
- Note any considerations
|
| 138 |
+
|
| 139 |
+
```
|
| 140 |
+
## Quick Exploration
|
| 141 |
+
|
| 142 |
+
[Your brief analysis—what you found, any considerations]
|
| 143 |
+
|
| 144 |
+
┌─────────────────────────────────────────┐
|
| 145 |
+
│ [Optional: ASCII diagram if helpful] │
|
| 146 |
+
└─────────────────────────────────────────┘
|
| 147 |
+
|
| 148 |
+
Explore mode (`/opsx:explore`) is for this kind of thinking—investigating before implementing. You can use it anytime you need to think through a problem.
|
| 149 |
+
|
| 150 |
+
Now let's create a change to hold our work.
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
**PAUSE** - Wait for user acknowledgment before proceeding.
|
| 154 |
+
|
| 155 |
+
---
|
| 156 |
+
|
| 157 |
+
## Phase 4: Create the Change
|
| 158 |
+
|
| 159 |
+
**EXPLAIN:**
|
| 160 |
+
```
|
| 161 |
+
## Creating a Change
|
| 162 |
+
|
| 163 |
+
A "change" in OpenSpec is a container for all the thinking and planning around a piece of work. It lives in `openspec/changes/<name>/` and holds your artifacts—proposal, specs, design, tasks.
|
| 164 |
+
|
| 165 |
+
Let me create one for our task.
|
| 166 |
+
```
|
| 167 |
+
|
| 168 |
+
**DO:** Create the change with a derived kebab-case name:
|
| 169 |
+
```bash
|
| 170 |
+
openspec new change "<derived-name>"
|
| 171 |
+
```
|
| 172 |
+
|
| 173 |
+
**SHOW:**
|
| 174 |
+
```
|
| 175 |
+
Created: `openspec/changes/<name>/`
|
| 176 |
+
|
| 177 |
+
The folder structure:
|
| 178 |
+
```
|
| 179 |
+
openspec/changes/<name>/
|
| 180 |
+
├── proposal.md ← Why we're doing this (empty, we'll fill it)
|
| 181 |
+
├── design.md ← How we'll build it (empty)
|
| 182 |
+
├── specs/ ← Detailed requirements (empty)
|
| 183 |
+
└── tasks.md ← Implementation checklist (empty)
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
Now let's fill in the first artifact—the proposal.
|
| 187 |
+
```
|
| 188 |
+
|
| 189 |
+
---
|
| 190 |
+
|
| 191 |
+
## Phase 5: Proposal
|
| 192 |
+
|
| 193 |
+
**EXPLAIN:**
|
| 194 |
+
```
|
| 195 |
+
## The Proposal
|
| 196 |
+
|
| 197 |
+
The proposal captures **why** we're making this change and **what** it involves at a high level. It's the "elevator pitch" for the work.
|
| 198 |
+
|
| 199 |
+
I'll draft one based on our task.
|
| 200 |
+
```
|
| 201 |
+
|
| 202 |
+
**DO:** Draft the proposal content (don't save yet):
|
| 203 |
+
|
| 204 |
+
```
|
| 205 |
+
Here's a draft proposal:
|
| 206 |
+
|
| 207 |
+
---
|
| 208 |
+
|
| 209 |
+
## Why
|
| 210 |
+
|
| 211 |
+
[1-2 sentences explaining the problem/opportunity]
|
| 212 |
+
|
| 213 |
+
## What Changes
|
| 214 |
+
|
| 215 |
+
[Bullet points of what will be different]
|
| 216 |
+
|
| 217 |
+
## Capabilities
|
| 218 |
+
|
| 219 |
+
### New Capabilities
|
| 220 |
+
- `<capability-name>`: [brief description]
|
| 221 |
+
|
| 222 |
+
### Modified Capabilities
|
| 223 |
+
<!-- If modifying existing behavior -->
|
| 224 |
+
|
| 225 |
+
## Impact
|
| 226 |
+
|
| 227 |
+
- `src/path/to/file.ts`: [what changes]
|
| 228 |
+
- [other files if applicable]
|
| 229 |
+
|
| 230 |
+
---
|
| 231 |
+
|
| 232 |
+
Does this capture the intent? I can adjust before we save it.
|
| 233 |
+
```
|
| 234 |
+
|
| 235 |
+
**PAUSE** - Wait for user approval/feedback.
|
| 236 |
+
|
| 237 |
+
After approval, save the proposal:
|
| 238 |
+
```bash
|
| 239 |
+
openspec instructions proposal --change "<name>" --json
|
| 240 |
+
```
|
| 241 |
+
Then write the content to `openspec/changes/<name>/proposal.md`.
|
| 242 |
+
|
| 243 |
+
```
|
| 244 |
+
Proposal saved. This is your "why" document—you can always come back and refine it as understanding evolves.
|
| 245 |
+
|
| 246 |
+
Next up: specs.
|
| 247 |
+
```
|
| 248 |
+
|
| 249 |
+
---
|
| 250 |
+
|
| 251 |
+
## Phase 6: Specs
|
| 252 |
+
|
| 253 |
+
**EXPLAIN:**
|
| 254 |
+
```
|
| 255 |
+
## Specs
|
| 256 |
+
|
| 257 |
+
Specs define **what** we're building in precise, testable terms. They use a requirement/scenario format that makes expected behavior crystal clear.
|
| 258 |
+
|
| 259 |
+
For a small task like this, we might only need one spec file.
|
| 260 |
+
```
|
| 261 |
+
|
| 262 |
+
**DO:** Create the spec file:
|
| 263 |
+
```bash
|
| 264 |
+
# Unix/macOS
|
| 265 |
+
mkdir -p openspec/changes/<name>/specs/<capability-name>
|
| 266 |
+
# Windows (PowerShell)
|
| 267 |
+
# New-Item -ItemType Directory -Force -Path "openspec/changes/<name>/specs/<capability-name>"
|
| 268 |
+
```
|
| 269 |
+
|
| 270 |
+
Draft the spec content:
|
| 271 |
+
|
| 272 |
+
```
|
| 273 |
+
Here's the spec:
|
| 274 |
+
|
| 275 |
+
---
|
| 276 |
+
|
| 277 |
+
## ADDED Requirements
|
| 278 |
+
|
| 279 |
+
### Requirement: <Name>
|
| 280 |
+
|
| 281 |
+
<Description of what the system should do>
|
| 282 |
+
|
| 283 |
+
#### Scenario: <Scenario name>
|
| 284 |
+
|
| 285 |
+
- **WHEN** <trigger condition>
|
| 286 |
+
- **THEN** <expected outcome>
|
| 287 |
+
- **AND** <additional outcome if needed>
|
| 288 |
+
|
| 289 |
+
---
|
| 290 |
+
|
| 291 |
+
This format—WHEN/THEN/AND—makes requirements testable. You can literally read them as test cases.
|
| 292 |
+
```
|
| 293 |
+
|
| 294 |
+
Save to `openspec/changes/<name>/specs/<capability>/spec.md`.
|
| 295 |
+
|
| 296 |
+
---
|
| 297 |
+
|
| 298 |
+
## Phase 7: Design
|
| 299 |
+
|
| 300 |
+
**EXPLAIN:**
|
| 301 |
+
```
|
| 302 |
+
## Design
|
| 303 |
+
|
| 304 |
+
The design captures **how** we'll build it—technical decisions, tradeoffs, approach.
|
| 305 |
+
|
| 306 |
+
For small changes, this might be brief. That's fine—not every change needs deep design discussion.
|
| 307 |
+
```
|
| 308 |
+
|
| 309 |
+
**DO:** Draft design.md:
|
| 310 |
+
|
| 311 |
+
```
|
| 312 |
+
Here's the design:
|
| 313 |
+
|
| 314 |
+
---
|
| 315 |
+
|
| 316 |
+
## Context
|
| 317 |
+
|
| 318 |
+
[Brief context about the current state]
|
| 319 |
+
|
| 320 |
+
## Goals / Non-Goals
|
| 321 |
+
|
| 322 |
+
**Goals:**
|
| 323 |
+
- [What we're trying to achieve]
|
| 324 |
+
|
| 325 |
+
**Non-Goals:**
|
| 326 |
+
- [What's explicitly out of scope]
|
| 327 |
+
|
| 328 |
+
## Decisions
|
| 329 |
+
|
| 330 |
+
### Decision 1: [Key decision]
|
| 331 |
+
|
| 332 |
+
[Explanation of approach and rationale]
|
| 333 |
+
|
| 334 |
+
---
|
| 335 |
+
|
| 336 |
+
For a small task, this captures the key decisions without over-engineering.
|
| 337 |
+
```
|
| 338 |
+
|
| 339 |
+
Save to `openspec/changes/<name>/design.md`.
|
| 340 |
+
|
| 341 |
+
---
|
| 342 |
+
|
| 343 |
+
## Phase 8: Tasks
|
| 344 |
+
|
| 345 |
+
**EXPLAIN:**
|
| 346 |
+
```
|
| 347 |
+
## Tasks
|
| 348 |
+
|
| 349 |
+
Finally, we break the work into implementation tasks—checkboxes that drive the apply phase.
|
| 350 |
+
|
| 351 |
+
These should be small, clear, and in logical order.
|
| 352 |
+
```
|
| 353 |
+
|
| 354 |
+
**DO:** Generate tasks based on specs and design:
|
| 355 |
+
|
| 356 |
+
```
|
| 357 |
+
Here are the implementation tasks:
|
| 358 |
+
|
| 359 |
+
---
|
| 360 |
+
|
| 361 |
+
## 1. [Category or file]
|
| 362 |
+
|
| 363 |
+
- [ ] 1.1 [Specific task]
|
| 364 |
+
- [ ] 1.2 [Specific task]
|
| 365 |
+
|
| 366 |
+
## 2. Verify
|
| 367 |
+
|
| 368 |
+
- [ ] 2.1 [Verification step]
|
| 369 |
+
|
| 370 |
+
---
|
| 371 |
+
|
| 372 |
+
Each checkbox becomes a unit of work in the apply phase. Ready to implement?
|
| 373 |
+
```
|
| 374 |
+
|
| 375 |
+
**PAUSE** - Wait for user to confirm they're ready to implement.
|
| 376 |
+
|
| 377 |
+
Save to `openspec/changes/<name>/tasks.md`.
|
| 378 |
+
|
| 379 |
+
---
|
| 380 |
+
|
| 381 |
+
## Phase 9: Apply (Implementation)
|
| 382 |
+
|
| 383 |
+
**EXPLAIN:**
|
| 384 |
+
```
|
| 385 |
+
## Implementation
|
| 386 |
+
|
| 387 |
+
Now we implement each task, checking them off as we go. I'll announce each one and occasionally note how the specs/design informed the approach.
|
| 388 |
+
```
|
| 389 |
+
|
| 390 |
+
**DO:** For each task:
|
| 391 |
+
|
| 392 |
+
1. Announce: "Working on task N: [description]"
|
| 393 |
+
2. Implement the change in the codebase
|
| 394 |
+
3. Reference specs/design naturally: "The spec says X, so I'm doing Y"
|
| 395 |
+
4. Mark complete in tasks.md: `- [ ]` → `- [x]`
|
| 396 |
+
5. Brief status: "✓ Task N complete"
|
| 397 |
+
|
| 398 |
+
Keep narration light—don't over-explain every line of code.
|
| 399 |
+
|
| 400 |
+
After all tasks:
|
| 401 |
+
|
| 402 |
+
```
|
| 403 |
+
## Implementation Complete
|
| 404 |
+
|
| 405 |
+
All tasks done:
|
| 406 |
+
- [x] Task 1
|
| 407 |
+
- [x] Task 2
|
| 408 |
+
- [x] ...
|
| 409 |
+
|
| 410 |
+
The change is implemented! One more step—let's archive it.
|
| 411 |
+
```
|
| 412 |
+
|
| 413 |
+
---
|
| 414 |
+
|
| 415 |
+
## Phase 10: Archive
|
| 416 |
+
|
| 417 |
+
**EXPLAIN:**
|
| 418 |
+
```
|
| 419 |
+
## Archiving
|
| 420 |
+
|
| 421 |
+
When a change is complete, we archive it. This moves it from `openspec/changes/` to `openspec/changes/archive/YYYY-MM-DD-<name>/`.
|
| 422 |
+
|
| 423 |
+
Archived changes become your project's decision history—you can always find them later to understand why something was built a certain way.
|
| 424 |
+
```
|
| 425 |
+
|
| 426 |
+
**DO:**
|
| 427 |
+
```bash
|
| 428 |
+
openspec archive "<name>"
|
| 429 |
+
```
|
| 430 |
+
|
| 431 |
+
**SHOW:**
|
| 432 |
+
```
|
| 433 |
+
Archived to: `openspec/changes/archive/YYYY-MM-DD-<name>/`
|
| 434 |
+
|
| 435 |
+
The change is now part of your project's history. The code is in your codebase, the decision record is preserved.
|
| 436 |
+
```
|
| 437 |
+
|
| 438 |
+
---
|
| 439 |
+
|
| 440 |
+
## Phase 11: Recap & Next Steps
|
| 441 |
+
|
| 442 |
+
```
|
| 443 |
+
## Congratulations!
|
| 444 |
+
|
| 445 |
+
You just completed a full OpenSpec cycle:
|
| 446 |
+
|
| 447 |
+
1. **Explore** - Thought through the problem
|
| 448 |
+
2. **New** - Created a change container
|
| 449 |
+
3. **Proposal** - Captured WHY
|
| 450 |
+
4. **Specs** - Defined WHAT in detail
|
| 451 |
+
5. **Design** - Decided HOW
|
| 452 |
+
6. **Tasks** - Broke it into steps
|
| 453 |
+
7. **Apply** - Implemented the work
|
| 454 |
+
8. **Archive** - Preserved the record
|
| 455 |
+
|
| 456 |
+
This same rhythm works for any size change—a small fix or a major feature.
|
| 457 |
+
|
| 458 |
+
---
|
| 459 |
+
|
| 460 |
+
## Command Reference
|
| 461 |
+
|
| 462 |
+
**Core workflow:**
|
| 463 |
+
|
| 464 |
+
| Command | What it does |
|
| 465 |
+
|---------|--------------|
|
| 466 |
+
| `/opsx:propose` | Create a change and generate all artifacts |
|
| 467 |
+
| `/opsx:explore` | Think through problems before/during work |
|
| 468 |
+
| `/opsx:apply` | Implement tasks from a change |
|
| 469 |
+
| `/opsx:archive` | Archive a completed change |
|
| 470 |
+
|
| 471 |
+
**Additional commands:**
|
| 472 |
+
|
| 473 |
+
| Command | What it does |
|
| 474 |
+
|---------|--------------|
|
| 475 |
+
| `/opsx:new` | Start a new change, step through artifacts one at a time |
|
| 476 |
+
| `/opsx:continue` | Continue working on an existing change |
|
| 477 |
+
| `/opsx:ff` | Fast-forward: create all artifacts at once |
|
| 478 |
+
| `/opsx:verify` | Verify implementation matches artifacts |
|
| 479 |
+
|
| 480 |
+
---
|
| 481 |
+
|
| 482 |
+
## What's Next?
|
| 483 |
+
|
| 484 |
+
Try `/opsx:propose` on something you actually want to build. You've got the rhythm now!
|
| 485 |
+
```
|
| 486 |
+
|
| 487 |
+
---
|
| 488 |
+
|
| 489 |
+
## Graceful Exit Handling
|
| 490 |
+
|
| 491 |
+
### User wants to stop mid-way
|
| 492 |
+
|
| 493 |
+
If the user says they need to stop, want to pause, or seem disengaged:
|
| 494 |
+
|
| 495 |
+
```
|
| 496 |
+
No problem! Your change is saved at `openspec/changes/<name>/`.
|
| 497 |
+
|
| 498 |
+
To pick up where we left off later:
|
| 499 |
+
- `/opsx:continue <name>` - Resume artifact creation
|
| 500 |
+
- `/opsx:apply <name>` - Jump to implementation (if tasks exist)
|
| 501 |
+
|
| 502 |
+
The work won't be lost. Come back whenever you're ready.
|
| 503 |
+
```
|
| 504 |
+
|
| 505 |
+
Exit gracefully without pressure.
|
| 506 |
+
|
| 507 |
+
### User just wants command reference
|
| 508 |
+
|
| 509 |
+
If the user says they just want to see the commands or skip the tutorial:
|
| 510 |
+
|
| 511 |
+
```
|
| 512 |
+
## OpenSpec Quick Reference
|
| 513 |
+
|
| 514 |
+
**Core workflow:**
|
| 515 |
+
|
| 516 |
+
| Command | What it does |
|
| 517 |
+
|---------|--------------|
|
| 518 |
+
| `/opsx:propose <name>` | Create a change and generate all artifacts |
|
| 519 |
+
| `/opsx:explore` | Think through problems (no code changes) |
|
| 520 |
+
| `/opsx:apply <name>` | Implement tasks |
|
| 521 |
+
| `/opsx:archive <name>` | Archive when done |
|
| 522 |
+
|
| 523 |
+
**Additional commands:**
|
| 524 |
+
|
| 525 |
+
| Command | What it does |
|
| 526 |
+
|---------|--------------|
|
| 527 |
+
| `/opsx:new <name>` | Start a new change, step by step |
|
| 528 |
+
| `/opsx:continue <name>` | Continue an existing change |
|
| 529 |
+
| `/opsx:ff <name>` | Fast-forward: all artifacts at once |
|
| 530 |
+
| `/opsx:verify <name>` | Verify implementation |
|
| 531 |
+
|
| 532 |
+
Try `/opsx:propose` to start your first change.
|
| 533 |
+
```
|
| 534 |
+
|
| 535 |
+
Exit gracefully.
|
| 536 |
+
|
| 537 |
+
---
|
| 538 |
+
|
| 539 |
+
## Guardrails
|
| 540 |
+
|
| 541 |
+
- **Follow the EXPLAIN → DO → SHOW → PAUSE pattern** at key transitions (after explore, after proposal draft, after tasks, after archive)
|
| 542 |
+
- **Keep narration light** during implementation—teach without lecturing
|
| 543 |
+
- **Don't skip phases** even if the change is small—the goal is teaching the workflow
|
| 544 |
+
- **Pause for acknowledgment** at marked points, but don't over-pause
|
| 545 |
+
- **Handle exits gracefully**—never pressure the user to continue
|
| 546 |
+
- **Use real codebase tasks**—don't simulate or use fake examples
|
| 547 |
+
- **Adjust scope gently**—guide toward smaller tasks but respect user choice
|
.github/prompts/opsx-sync.prompt.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Sync delta specs from a change to main specs
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Sync delta specs from a change to main specs.
|
| 6 |
+
|
| 7 |
+
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
| 8 |
+
|
| 9 |
+
**Input**: Optionally specify a change name after `/opsx:sync` (e.g., `/opsx:sync add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 10 |
+
|
| 11 |
+
**Steps**
|
| 12 |
+
|
| 13 |
+
1. **If no change name provided, prompt for selection**
|
| 14 |
+
|
| 15 |
+
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
| 16 |
+
|
| 17 |
+
Show changes that have delta specs (under `specs/` directory).
|
| 18 |
+
|
| 19 |
+
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
| 20 |
+
|
| 21 |
+
2. **Find delta specs**
|
| 22 |
+
|
| 23 |
+
Look for delta spec files in `openspec/changes/<name>/specs/*/spec.md`.
|
| 24 |
+
|
| 25 |
+
Each delta spec file contains sections like:
|
| 26 |
+
- `## ADDED Requirements` - New requirements to add
|
| 27 |
+
- `## MODIFIED Requirements` - Changes to existing requirements
|
| 28 |
+
- `## REMOVED Requirements` - Requirements to remove
|
| 29 |
+
- `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
|
| 30 |
+
|
| 31 |
+
If no delta specs found, inform user and stop.
|
| 32 |
+
|
| 33 |
+
3. **For each delta spec, apply changes to main specs**
|
| 34 |
+
|
| 35 |
+
For each capability with a delta spec at `openspec/changes/<name>/specs/<capability>/spec.md`:
|
| 36 |
+
|
| 37 |
+
a. **Read the delta spec** to understand the intended changes
|
| 38 |
+
|
| 39 |
+
b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
|
| 40 |
+
|
| 41 |
+
c. **Apply changes intelligently**:
|
| 42 |
+
|
| 43 |
+
**ADDED Requirements:**
|
| 44 |
+
- If requirement doesn't exist in main spec → add it
|
| 45 |
+
- If requirement already exists → update it to match (treat as implicit MODIFIED)
|
| 46 |
+
|
| 47 |
+
**MODIFIED Requirements:**
|
| 48 |
+
- Find the requirement in main spec
|
| 49 |
+
- Apply the changes - this can be:
|
| 50 |
+
- Adding new scenarios (don't need to copy existing ones)
|
| 51 |
+
- Modifying existing scenarios
|
| 52 |
+
- Changing the requirement description
|
| 53 |
+
- Preserve scenarios/content not mentioned in the delta
|
| 54 |
+
|
| 55 |
+
**REMOVED Requirements:**
|
| 56 |
+
- Remove the entire requirement block from main spec
|
| 57 |
+
|
| 58 |
+
**RENAMED Requirements:**
|
| 59 |
+
- Find the FROM requirement, rename to TO
|
| 60 |
+
|
| 61 |
+
d. **Create new main spec** if capability doesn't exist yet:
|
| 62 |
+
- Create `openspec/specs/<capability>/spec.md`
|
| 63 |
+
- Add Purpose section (can be brief, mark as TBD)
|
| 64 |
+
- Add Requirements section with the ADDED requirements
|
| 65 |
+
|
| 66 |
+
4. **Show summary**
|
| 67 |
+
|
| 68 |
+
After applying all changes, summarize:
|
| 69 |
+
- Which capabilities were updated
|
| 70 |
+
- What changes were made (requirements added/modified/removed/renamed)
|
| 71 |
+
|
| 72 |
+
**Delta Spec Format Reference**
|
| 73 |
+
|
| 74 |
+
```markdown
|
| 75 |
+
## ADDED Requirements
|
| 76 |
+
|
| 77 |
+
### Requirement: New Feature
|
| 78 |
+
The system SHALL do something new.
|
| 79 |
+
|
| 80 |
+
#### Scenario: Basic case
|
| 81 |
+
- **WHEN** user does X
|
| 82 |
+
- **THEN** system does Y
|
| 83 |
+
|
| 84 |
+
## MODIFIED Requirements
|
| 85 |
+
|
| 86 |
+
### Requirement: Existing Feature
|
| 87 |
+
#### Scenario: New scenario to add
|
| 88 |
+
- **WHEN** user does A
|
| 89 |
+
- **THEN** system does B
|
| 90 |
+
|
| 91 |
+
## REMOVED Requirements
|
| 92 |
+
|
| 93 |
+
### Requirement: Deprecated Feature
|
| 94 |
+
|
| 95 |
+
## RENAMED Requirements
|
| 96 |
+
|
| 97 |
+
- FROM: `### Requirement: Old Name`
|
| 98 |
+
- TO: `### Requirement: New Name`
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
**Key Principle: Intelligent Merging**
|
| 102 |
+
|
| 103 |
+
Unlike programmatic merging, you can apply **partial updates**:
|
| 104 |
+
- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
|
| 105 |
+
- The delta represents *intent*, not a wholesale replacement
|
| 106 |
+
- Use your judgment to merge changes sensibly
|
| 107 |
+
|
| 108 |
+
**Output On Success**
|
| 109 |
+
|
| 110 |
+
```
|
| 111 |
+
## Specs Synced: <change-name>
|
| 112 |
+
|
| 113 |
+
Updated main specs:
|
| 114 |
+
|
| 115 |
+
**<capability-1>**:
|
| 116 |
+
- Added requirement: "New Feature"
|
| 117 |
+
- Modified requirement: "Existing Feature" (added 1 scenario)
|
| 118 |
+
|
| 119 |
+
**<capability-2>**:
|
| 120 |
+
- Created new spec file
|
| 121 |
+
- Added requirement: "Another Feature"
|
| 122 |
+
|
| 123 |
+
Main specs are now updated. The change remains active - archive when implementation is complete.
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
**Guardrails**
|
| 127 |
+
- Read both delta and main specs before making changes
|
| 128 |
+
- Preserve existing content not mentioned in delta
|
| 129 |
+
- If something is unclear, ask for clarification
|
| 130 |
+
- Show what you're changing as you go
|
| 131 |
+
- The operation should be idempotent - running twice should give same result
|
.github/prompts/opsx-verify.prompt.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
description: Verify implementation matches change artifacts before archiving
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
Verify that an implementation matches the change artifacts (specs, tasks, design).
|
| 6 |
+
|
| 7 |
+
**Input**: Optionally specify a change name after `/opsx:verify` (e.g., `/opsx:verify add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 8 |
+
|
| 9 |
+
**Steps**
|
| 10 |
+
|
| 11 |
+
1. **If no change name provided, prompt for selection**
|
| 12 |
+
|
| 13 |
+
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
| 14 |
+
|
| 15 |
+
Show changes that have implementation tasks (tasks artifact exists).
|
| 16 |
+
Include the schema used for each change if available.
|
| 17 |
+
Mark changes with incomplete tasks as "(In Progress)".
|
| 18 |
+
|
| 19 |
+
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
| 20 |
+
|
| 21 |
+
2. **Check status to understand the schema**
|
| 22 |
+
```bash
|
| 23 |
+
openspec status --change "<name>" --json
|
| 24 |
+
```
|
| 25 |
+
Parse the JSON to understand:
|
| 26 |
+
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
| 27 |
+
- Which artifacts exist for this change
|
| 28 |
+
|
| 29 |
+
3. **Get the change directory and load artifacts**
|
| 30 |
+
|
| 31 |
+
```bash
|
| 32 |
+
openspec instructions apply --change "<name>" --json
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
This returns the change directory and context files. Read all available artifacts from `contextFiles`.
|
| 36 |
+
|
| 37 |
+
4. **Initialize verification report structure**
|
| 38 |
+
|
| 39 |
+
Create a report structure with three dimensions:
|
| 40 |
+
- **Completeness**: Track tasks and spec coverage
|
| 41 |
+
- **Correctness**: Track requirement implementation and scenario coverage
|
| 42 |
+
- **Coherence**: Track design adherence and pattern consistency
|
| 43 |
+
|
| 44 |
+
Each dimension can have CRITICAL, WARNING, or SUGGESTION issues.
|
| 45 |
+
|
| 46 |
+
5. **Verify Completeness**
|
| 47 |
+
|
| 48 |
+
**Task Completion**:
|
| 49 |
+
- If tasks.md exists in contextFiles, read it
|
| 50 |
+
- Parse checkboxes: `- [ ]` (incomplete) vs `- [x]` (complete)
|
| 51 |
+
- Count complete vs total tasks
|
| 52 |
+
- If incomplete tasks exist:
|
| 53 |
+
- Add CRITICAL issue for each incomplete task
|
| 54 |
+
- Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
|
| 55 |
+
|
| 56 |
+
**Spec Coverage**:
|
| 57 |
+
- If delta specs exist in `openspec/changes/<name>/specs/`:
|
| 58 |
+
- Extract all requirements (marked with "### Requirement:")
|
| 59 |
+
- For each requirement:
|
| 60 |
+
- Search codebase for keywords related to the requirement
|
| 61 |
+
- Assess if implementation likely exists
|
| 62 |
+
- If requirements appear unimplemented:
|
| 63 |
+
- Add CRITICAL issue: "Requirement not found: <requirement name>"
|
| 64 |
+
- Recommendation: "Implement requirement X: <description>"
|
| 65 |
+
|
| 66 |
+
6. **Verify Correctness**
|
| 67 |
+
|
| 68 |
+
**Requirement Implementation Mapping**:
|
| 69 |
+
- For each requirement from delta specs:
|
| 70 |
+
- Search codebase for implementation evidence
|
| 71 |
+
- If found, note file paths and line ranges
|
| 72 |
+
- Assess if implementation matches requirement intent
|
| 73 |
+
- If divergence detected:
|
| 74 |
+
- Add WARNING: "Implementation may diverge from spec: <details>"
|
| 75 |
+
- Recommendation: "Review <file>:<lines> against requirement X"
|
| 76 |
+
|
| 77 |
+
**Scenario Coverage**:
|
| 78 |
+
- For each scenario in delta specs (marked with "#### Scenario:"):
|
| 79 |
+
- Check if conditions are handled in code
|
| 80 |
+
- Check if tests exist covering the scenario
|
| 81 |
+
- If scenario appears uncovered:
|
| 82 |
+
- Add WARNING: "Scenario not covered: <scenario name>"
|
| 83 |
+
- Recommendation: "Add test or implementation for scenario: <description>"
|
| 84 |
+
|
| 85 |
+
7. **Verify Coherence**
|
| 86 |
+
|
| 87 |
+
**Design Adherence**:
|
| 88 |
+
- If design.md exists in contextFiles:
|
| 89 |
+
- Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
|
| 90 |
+
- Verify implementation follows those decisions
|
| 91 |
+
- If contradiction detected:
|
| 92 |
+
- Add WARNING: "Design decision not followed: <decision>"
|
| 93 |
+
- Recommendation: "Update implementation or revise design.md to match reality"
|
| 94 |
+
- If no design.md: Skip design adherence check, note "No design.md to verify against"
|
| 95 |
+
|
| 96 |
+
**Code Pattern Consistency**:
|
| 97 |
+
- Review new code for consistency with project patterns
|
| 98 |
+
- Check file naming, directory structure, coding style
|
| 99 |
+
- If significant deviations found:
|
| 100 |
+
- Add SUGGESTION: "Code pattern deviation: <details>"
|
| 101 |
+
- Recommendation: "Consider following project pattern: <example>"
|
| 102 |
+
|
| 103 |
+
8. **Generate Verification Report**
|
| 104 |
+
|
| 105 |
+
**Summary Scorecard**:
|
| 106 |
+
```
|
| 107 |
+
## Verification Report: <change-name>
|
| 108 |
+
|
| 109 |
+
### Summary
|
| 110 |
+
| Dimension | Status |
|
| 111 |
+
|--------------|------------------|
|
| 112 |
+
| Completeness | X/Y tasks, N reqs|
|
| 113 |
+
| Correctness | M/N reqs covered |
|
| 114 |
+
| Coherence | Followed/Issues |
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
**Issues by Priority**:
|
| 118 |
+
|
| 119 |
+
1. **CRITICAL** (Must fix before archive):
|
| 120 |
+
- Incomplete tasks
|
| 121 |
+
- Missing requirement implementations
|
| 122 |
+
- Each with specific, actionable recommendation
|
| 123 |
+
|
| 124 |
+
2. **WARNING** (Should fix):
|
| 125 |
+
- Spec/design divergences
|
| 126 |
+
- Missing scenario coverage
|
| 127 |
+
- Each with specific recommendation
|
| 128 |
+
|
| 129 |
+
3. **SUGGESTION** (Nice to fix):
|
| 130 |
+
- Pattern inconsistencies
|
| 131 |
+
- Minor improvements
|
| 132 |
+
- Each with specific recommendation
|
| 133 |
+
|
| 134 |
+
**Final Assessment**:
|
| 135 |
+
- If CRITICAL issues: "X critical issue(s) found. Fix before archiving."
|
| 136 |
+
- If only warnings: "No critical issues. Y warning(s) to consider. Ready for archive (with noted improvements)."
|
| 137 |
+
- If all clear: "All checks passed. Ready for archive."
|
| 138 |
+
|
| 139 |
+
**Verification Heuristics**
|
| 140 |
+
|
| 141 |
+
- **Completeness**: Focus on objective checklist items (checkboxes, requirements list)
|
| 142 |
+
- **Correctness**: Use keyword search, file path analysis, reasonable inference - don't require perfect certainty
|
| 143 |
+
- **Coherence**: Look for glaring inconsistencies, don't nitpick style
|
| 144 |
+
- **False Positives**: When uncertain, prefer SUGGESTION over WARNING, WARNING over CRITICAL
|
| 145 |
+
- **Actionability**: Every issue must have a specific recommendation with file/line references where applicable
|
| 146 |
+
|
| 147 |
+
**Graceful Degradation**
|
| 148 |
+
|
| 149 |
+
- If only tasks.md exists: verify task completion only, skip spec/design checks
|
| 150 |
+
- If tasks + specs exist: verify completeness and correctness, skip design
|
| 151 |
+
- If full artifacts: verify all three dimensions
|
| 152 |
+
- Always note which checks were skipped and why
|
| 153 |
+
|
| 154 |
+
**Output Format**
|
| 155 |
+
|
| 156 |
+
Use clear markdown with:
|
| 157 |
+
- Table for summary scorecard
|
| 158 |
+
- Grouped lists for issues (CRITICAL/WARNING/SUGGESTION)
|
| 159 |
+
- Code references in format: `file.ts:123`
|
| 160 |
+
- Specific, actionable recommendations
|
| 161 |
+
- No vague suggestions like "consider reviewing"
|
.github/skills/openspec-apply-change/SKILL.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-apply-change
|
| 3 |
+
description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Implement tasks from an OpenSpec change.
|
| 13 |
+
|
| 14 |
+
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 15 |
+
|
| 16 |
+
**Steps**
|
| 17 |
+
|
| 18 |
+
1. **Select the change**
|
| 19 |
+
|
| 20 |
+
If a name is provided, use it. Otherwise:
|
| 21 |
+
- Infer from conversation context if the user mentioned a change
|
| 22 |
+
- Auto-select if only one active change exists
|
| 23 |
+
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
| 24 |
+
|
| 25 |
+
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
| 26 |
+
|
| 27 |
+
2. **Check status to understand the schema**
|
| 28 |
+
```bash
|
| 29 |
+
openspec status --change "<name>" --json
|
| 30 |
+
```
|
| 31 |
+
Parse the JSON to understand:
|
| 32 |
+
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
| 33 |
+
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
| 34 |
+
|
| 35 |
+
3. **Get apply instructions**
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
openspec instructions apply --change "<name>" --json
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
This returns:
|
| 42 |
+
- Context file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)
|
| 43 |
+
- Progress (total, complete, remaining)
|
| 44 |
+
- Task list with status
|
| 45 |
+
- Dynamic instruction based on current state
|
| 46 |
+
|
| 47 |
+
**Handle states:**
|
| 48 |
+
- If `state: "blocked"` (missing artifacts): show message, suggest using openspec-continue-change
|
| 49 |
+
- If `state: "all_done"`: congratulate, suggest archive
|
| 50 |
+
- Otherwise: proceed to implementation
|
| 51 |
+
|
| 52 |
+
4. **Read context files**
|
| 53 |
+
|
| 54 |
+
Read the files listed in `contextFiles` from the apply instructions output.
|
| 55 |
+
The files depend on the schema being used:
|
| 56 |
+
- **spec-driven**: proposal, specs, design, tasks
|
| 57 |
+
- Other schemas: follow the contextFiles from CLI output
|
| 58 |
+
|
| 59 |
+
5. **Show current progress**
|
| 60 |
+
|
| 61 |
+
Display:
|
| 62 |
+
- Schema being used
|
| 63 |
+
- Progress: "N/M tasks complete"
|
| 64 |
+
- Remaining tasks overview
|
| 65 |
+
- Dynamic instruction from CLI
|
| 66 |
+
|
| 67 |
+
6. **Implement tasks (loop until done or blocked)**
|
| 68 |
+
|
| 69 |
+
For each pending task:
|
| 70 |
+
- Show which task is being worked on
|
| 71 |
+
- Make the code changes required
|
| 72 |
+
- Keep changes minimal and focused
|
| 73 |
+
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
| 74 |
+
- Continue to next task
|
| 75 |
+
|
| 76 |
+
**Pause if:**
|
| 77 |
+
- Task is unclear → ask for clarification
|
| 78 |
+
- Implementation reveals a design issue → suggest updating artifacts
|
| 79 |
+
- Error or blocker encountered → report and wait for guidance
|
| 80 |
+
- User interrupts
|
| 81 |
+
|
| 82 |
+
7. **On completion or pause, show status**
|
| 83 |
+
|
| 84 |
+
Display:
|
| 85 |
+
- Tasks completed this session
|
| 86 |
+
- Overall progress: "N/M tasks complete"
|
| 87 |
+
- If all done: suggest archive
|
| 88 |
+
- If paused: explain why and wait for guidance
|
| 89 |
+
|
| 90 |
+
**Output During Implementation**
|
| 91 |
+
|
| 92 |
+
```
|
| 93 |
+
## Implementing: <change-name> (schema: <schema-name>)
|
| 94 |
+
|
| 95 |
+
Working on task 3/7: <task description>
|
| 96 |
+
[...implementation happening...]
|
| 97 |
+
✓ Task complete
|
| 98 |
+
|
| 99 |
+
Working on task 4/7: <task description>
|
| 100 |
+
[...implementation happening...]
|
| 101 |
+
✓ Task complete
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
**Output On Completion**
|
| 105 |
+
|
| 106 |
+
```
|
| 107 |
+
## Implementation Complete
|
| 108 |
+
|
| 109 |
+
**Change:** <change-name>
|
| 110 |
+
**Schema:** <schema-name>
|
| 111 |
+
**Progress:** 7/7 tasks complete ✓
|
| 112 |
+
|
| 113 |
+
### Completed This Session
|
| 114 |
+
- [x] Task 1
|
| 115 |
+
- [x] Task 2
|
| 116 |
+
...
|
| 117 |
+
|
| 118 |
+
All tasks complete! Ready to archive this change.
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
**Output On Pause (Issue Encountered)**
|
| 122 |
+
|
| 123 |
+
```
|
| 124 |
+
## Implementation Paused
|
| 125 |
+
|
| 126 |
+
**Change:** <change-name>
|
| 127 |
+
**Schema:** <schema-name>
|
| 128 |
+
**Progress:** 4/7 tasks complete
|
| 129 |
+
|
| 130 |
+
### Issue Encountered
|
| 131 |
+
<description of the issue>
|
| 132 |
+
|
| 133 |
+
**Options:**
|
| 134 |
+
1. <option 1>
|
| 135 |
+
2. <option 2>
|
| 136 |
+
3. Other approach
|
| 137 |
+
|
| 138 |
+
What would you like to do?
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
**Guardrails**
|
| 142 |
+
- Keep going through tasks until done or blocked
|
| 143 |
+
- Always read context files before starting (from the apply instructions output)
|
| 144 |
+
- If task is ambiguous, pause and ask before implementing
|
| 145 |
+
- If implementation reveals issues, pause and suggest artifact updates
|
| 146 |
+
- Keep code changes minimal and scoped to each task
|
| 147 |
+
- Update task checkbox immediately after completing each task
|
| 148 |
+
- Pause on errors, blockers, or unclear requirements - don't guess
|
| 149 |
+
- Use contextFiles from CLI output, don't assume specific file names
|
| 150 |
+
|
| 151 |
+
**Fluid Workflow Integration**
|
| 152 |
+
|
| 153 |
+
This skill supports the "actions on a change" model:
|
| 154 |
+
|
| 155 |
+
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
| 156 |
+
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|
.github/skills/openspec-archive-change/SKILL.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-archive-change
|
| 3 |
+
description: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Archive a completed change in the experimental workflow.
|
| 13 |
+
|
| 14 |
+
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 15 |
+
|
| 16 |
+
**Steps**
|
| 17 |
+
|
| 18 |
+
1. **If no change name provided, prompt for selection**
|
| 19 |
+
|
| 20 |
+
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
| 21 |
+
|
| 22 |
+
Show only active changes (not already archived).
|
| 23 |
+
Include the schema used for each change if available.
|
| 24 |
+
|
| 25 |
+
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
| 26 |
+
|
| 27 |
+
2. **Check artifact completion status**
|
| 28 |
+
|
| 29 |
+
Run `openspec status --change "<name>" --json` to check artifact completion.
|
| 30 |
+
|
| 31 |
+
Parse the JSON to understand:
|
| 32 |
+
- `schemaName`: The workflow being used
|
| 33 |
+
- `artifacts`: List of artifacts with their status (`done` or other)
|
| 34 |
+
|
| 35 |
+
**If any artifacts are not `done`:**
|
| 36 |
+
- Display warning listing incomplete artifacts
|
| 37 |
+
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
| 38 |
+
- Proceed if user confirms
|
| 39 |
+
|
| 40 |
+
3. **Check task completion status**
|
| 41 |
+
|
| 42 |
+
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
| 43 |
+
|
| 44 |
+
Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).
|
| 45 |
+
|
| 46 |
+
**If incomplete tasks found:**
|
| 47 |
+
- Display warning showing count of incomplete tasks
|
| 48 |
+
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
| 49 |
+
- Proceed if user confirms
|
| 50 |
+
|
| 51 |
+
**If no tasks file exists:** Proceed without task-related warning.
|
| 52 |
+
|
| 53 |
+
4. **Assess delta spec sync state**
|
| 54 |
+
|
| 55 |
+
Check for delta specs at `openspec/changes/<name>/specs/`. If none exist, proceed without sync prompt.
|
| 56 |
+
|
| 57 |
+
**If delta specs exist:**
|
| 58 |
+
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
| 59 |
+
- Determine what changes would be applied (adds, modifications, removals, renames)
|
| 60 |
+
- Show a combined summary before prompting
|
| 61 |
+
|
| 62 |
+
**Prompt options:**
|
| 63 |
+
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
| 64 |
+
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
| 65 |
+
|
| 66 |
+
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
| 67 |
+
|
| 68 |
+
5. **Perform the archive**
|
| 69 |
+
|
| 70 |
+
Create the archive directory if it doesn't exist:
|
| 71 |
+
```bash
|
| 72 |
+
mkdir -p openspec/changes/archive
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
| 76 |
+
|
| 77 |
+
**Check if target already exists:**
|
| 78 |
+
- If yes: Fail with error, suggest renaming existing archive or using different date
|
| 79 |
+
- If no: Move the change directory to archive
|
| 80 |
+
|
| 81 |
+
```bash
|
| 82 |
+
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
6. **Display summary**
|
| 86 |
+
|
| 87 |
+
Show archive completion summary including:
|
| 88 |
+
- Change name
|
| 89 |
+
- Schema that was used
|
| 90 |
+
- Archive location
|
| 91 |
+
- Whether specs were synced (if applicable)
|
| 92 |
+
- Note about any warnings (incomplete artifacts/tasks)
|
| 93 |
+
|
| 94 |
+
**Output On Success**
|
| 95 |
+
|
| 96 |
+
```
|
| 97 |
+
## Archive Complete
|
| 98 |
+
|
| 99 |
+
**Change:** <change-name>
|
| 100 |
+
**Schema:** <schema-name>
|
| 101 |
+
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
| 102 |
+
**Specs:** ✓ Synced to main specs (or "No delta specs" or "Sync skipped")
|
| 103 |
+
|
| 104 |
+
All artifacts complete. All tasks complete.
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
**Guardrails**
|
| 108 |
+
- Always prompt for change selection if not provided
|
| 109 |
+
- Use artifact graph (openspec status --json) for completion checking
|
| 110 |
+
- Don't block archive on warnings - just inform and confirm
|
| 111 |
+
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
| 112 |
+
- Show clear summary of what happened
|
| 113 |
+
- If sync is requested, use openspec-sync-specs approach (agent-driven)
|
| 114 |
+
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
.github/skills/openspec-bulk-archive-change/SKILL.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-bulk-archive-change
|
| 3 |
+
description: Archive multiple completed changes at once. Use when archiving several parallel changes.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Archive multiple completed changes in a single operation.
|
| 13 |
+
|
| 14 |
+
This skill allows you to batch-archive changes, handling spec conflicts intelligently by checking the codebase to determine what's actually implemented.
|
| 15 |
+
|
| 16 |
+
**Input**: None required (prompts for selection)
|
| 17 |
+
|
| 18 |
+
**Steps**
|
| 19 |
+
|
| 20 |
+
1. **Get active changes**
|
| 21 |
+
|
| 22 |
+
Run `openspec list --json` to get all active changes.
|
| 23 |
+
|
| 24 |
+
If no active changes exist, inform user and stop.
|
| 25 |
+
|
| 26 |
+
2. **Prompt for change selection**
|
| 27 |
+
|
| 28 |
+
Use **AskUserQuestion tool** with multi-select to let user choose changes:
|
| 29 |
+
- Show each change with its schema
|
| 30 |
+
- Include an option for "All changes"
|
| 31 |
+
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
| 32 |
+
|
| 33 |
+
**IMPORTANT**: Do NOT auto-select. Always let the user choose.
|
| 34 |
+
|
| 35 |
+
3. **Batch validation - gather status for all selected changes**
|
| 36 |
+
|
| 37 |
+
For each selected change, collect:
|
| 38 |
+
|
| 39 |
+
a. **Artifact status** - Run `openspec status --change "<name>" --json`
|
| 40 |
+
- Parse `schemaName` and `artifacts` list
|
| 41 |
+
- Note which artifacts are `done` vs other states
|
| 42 |
+
|
| 43 |
+
b. **Task completion** - Read `openspec/changes/<name>/tasks.md`
|
| 44 |
+
- Count `- [ ]` (incomplete) vs `- [x]` (complete)
|
| 45 |
+
- If no tasks file exists, note as "No tasks"
|
| 46 |
+
|
| 47 |
+
c. **Delta specs** - Check `openspec/changes/<name>/specs/` directory
|
| 48 |
+
- List which capability specs exist
|
| 49 |
+
- For each, extract requirement names (lines matching `### Requirement: <name>`)
|
| 50 |
+
|
| 51 |
+
4. **Detect spec conflicts**
|
| 52 |
+
|
| 53 |
+
Build a map of `capability -> [changes that touch it]`:
|
| 54 |
+
|
| 55 |
+
```
|
| 56 |
+
auth -> [change-a, change-b] <- CONFLICT (2+ changes)
|
| 57 |
+
api -> [change-c] <- OK (only 1 change)
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
A conflict exists when 2+ selected changes have delta specs for the same capability.
|
| 61 |
+
|
| 62 |
+
5. **Resolve conflicts agentically**
|
| 63 |
+
|
| 64 |
+
**For each conflict**, investigate the codebase:
|
| 65 |
+
|
| 66 |
+
a. **Read the delta specs** from each conflicting change to understand what each claims to add/modify
|
| 67 |
+
|
| 68 |
+
b. **Search the codebase** for implementation evidence:
|
| 69 |
+
- Look for code implementing requirements from each delta spec
|
| 70 |
+
- Check for related files, functions, or tests
|
| 71 |
+
|
| 72 |
+
c. **Determine resolution**:
|
| 73 |
+
- If only one change is actually implemented -> sync that one's specs
|
| 74 |
+
- If both implemented -> apply in chronological order (older first, newer overwrites)
|
| 75 |
+
- If neither implemented -> skip spec sync, warn user
|
| 76 |
+
|
| 77 |
+
d. **Record resolution** for each conflict:
|
| 78 |
+
- Which change's specs to apply
|
| 79 |
+
- In what order (if both)
|
| 80 |
+
- Rationale (what was found in codebase)
|
| 81 |
+
|
| 82 |
+
6. **Show consolidated status table**
|
| 83 |
+
|
| 84 |
+
Display a table summarizing all changes:
|
| 85 |
+
|
| 86 |
+
```
|
| 87 |
+
| Change | Artifacts | Tasks | Specs | Conflicts | Status |
|
| 88 |
+
|---------------------|-----------|-------|---------|-----------|--------|
|
| 89 |
+
| schema-management | Done | 5/5 | 2 delta | None | Ready |
|
| 90 |
+
| project-config | Done | 3/3 | 1 delta | None | Ready |
|
| 91 |
+
| add-oauth | Done | 4/4 | 1 delta | auth (!) | Ready* |
|
| 92 |
+
| add-verify-skill | 1 left | 2/5 | None | None | Warn |
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
For conflicts, show the resolution:
|
| 96 |
+
```
|
| 97 |
+
* Conflict resolution:
|
| 98 |
+
- auth spec: Will apply add-oauth then add-jwt (both implemented, chronological order)
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
For incomplete changes, show warnings:
|
| 102 |
+
```
|
| 103 |
+
Warnings:
|
| 104 |
+
- add-verify-skill: 1 incomplete artifact, 3 incomplete tasks
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
7. **Confirm batch operation**
|
| 108 |
+
|
| 109 |
+
Use **AskUserQuestion tool** with a single confirmation:
|
| 110 |
+
|
| 111 |
+
- "Archive N changes?" with options based on status
|
| 112 |
+
- Options might include:
|
| 113 |
+
- "Archive all N changes"
|
| 114 |
+
- "Archive only N ready changes (skip incomplete)"
|
| 115 |
+
- "Cancel"
|
| 116 |
+
|
| 117 |
+
If there are incomplete changes, make clear they'll be archived with warnings.
|
| 118 |
+
|
| 119 |
+
8. **Execute archive for each confirmed change**
|
| 120 |
+
|
| 121 |
+
Process changes in the determined order (respecting conflict resolution):
|
| 122 |
+
|
| 123 |
+
a. **Sync specs** if delta specs exist:
|
| 124 |
+
- Use the openspec-sync-specs approach (agent-driven intelligent merge)
|
| 125 |
+
- For conflicts, apply in resolved order
|
| 126 |
+
- Track if sync was done
|
| 127 |
+
|
| 128 |
+
b. **Perform the archive**:
|
| 129 |
+
```bash
|
| 130 |
+
mkdir -p openspec/changes/archive
|
| 131 |
+
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
c. **Track outcome** for each change:
|
| 135 |
+
- Success: archived successfully
|
| 136 |
+
- Failed: error during archive (record error)
|
| 137 |
+
- Skipped: user chose not to archive (if applicable)
|
| 138 |
+
|
| 139 |
+
9. **Display summary**
|
| 140 |
+
|
| 141 |
+
Show final results:
|
| 142 |
+
|
| 143 |
+
```
|
| 144 |
+
## Bulk Archive Complete
|
| 145 |
+
|
| 146 |
+
Archived 3 changes:
|
| 147 |
+
- schema-management-cli -> archive/2026-01-19-schema-management-cli/
|
| 148 |
+
- project-config -> archive/2026-01-19-project-config/
|
| 149 |
+
- add-oauth -> archive/2026-01-19-add-oauth/
|
| 150 |
+
|
| 151 |
+
Skipped 1 change:
|
| 152 |
+
- add-verify-skill (user chose not to archive incomplete)
|
| 153 |
+
|
| 154 |
+
Spec sync summary:
|
| 155 |
+
- 4 delta specs synced to main specs
|
| 156 |
+
- 1 conflict resolved (auth: applied both in chronological order)
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
If any failures:
|
| 160 |
+
```
|
| 161 |
+
Failed 1 change:
|
| 162 |
+
- some-change: Archive directory already exists
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
**Conflict Resolution Examples**
|
| 166 |
+
|
| 167 |
+
Example 1: Only one implemented
|
| 168 |
+
```
|
| 169 |
+
Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt]
|
| 170 |
+
|
| 171 |
+
Checking add-oauth:
|
| 172 |
+
- Delta adds "OAuth Provider Integration" requirement
|
| 173 |
+
- Searching codebase... found src/auth/oauth.ts implementing OAuth flow
|
| 174 |
+
|
| 175 |
+
Checking add-jwt:
|
| 176 |
+
- Delta adds "JWT Token Handling" requirement
|
| 177 |
+
- Searching codebase... no JWT implementation found
|
| 178 |
+
|
| 179 |
+
Resolution: Only add-oauth is implemented. Will sync add-oauth specs only.
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
Example 2: Both implemented
|
| 183 |
+
```
|
| 184 |
+
Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql]
|
| 185 |
+
|
| 186 |
+
Checking add-rest-api (created 2026-01-10):
|
| 187 |
+
- Delta adds "REST Endpoints" requirement
|
| 188 |
+
- Searching codebase... found src/api/rest.ts
|
| 189 |
+
|
| 190 |
+
Checking add-graphql (created 2026-01-15):
|
| 191 |
+
- Delta adds "GraphQL Schema" requirement
|
| 192 |
+
- Searching codebase... found src/api/graphql.ts
|
| 193 |
+
|
| 194 |
+
Resolution: Both implemented. Will apply add-rest-api specs first,
|
| 195 |
+
then add-graphql specs (chronological order, newer takes precedence).
|
| 196 |
+
```
|
| 197 |
+
|
| 198 |
+
**Output On Success**
|
| 199 |
+
|
| 200 |
+
```
|
| 201 |
+
## Bulk Archive Complete
|
| 202 |
+
|
| 203 |
+
Archived N changes:
|
| 204 |
+
- <change-1> -> archive/YYYY-MM-DD-<change-1>/
|
| 205 |
+
- <change-2> -> archive/YYYY-MM-DD-<change-2>/
|
| 206 |
+
|
| 207 |
+
Spec sync summary:
|
| 208 |
+
- N delta specs synced to main specs
|
| 209 |
+
- No conflicts (or: M conflicts resolved)
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
**Output On Partial Success**
|
| 213 |
+
|
| 214 |
+
```
|
| 215 |
+
## Bulk Archive Complete (partial)
|
| 216 |
+
|
| 217 |
+
Archived N changes:
|
| 218 |
+
- <change-1> -> archive/YYYY-MM-DD-<change-1>/
|
| 219 |
+
|
| 220 |
+
Skipped M changes:
|
| 221 |
+
- <change-2> (user chose not to archive incomplete)
|
| 222 |
+
|
| 223 |
+
Failed K changes:
|
| 224 |
+
- <change-3>: Archive directory already exists
|
| 225 |
+
```
|
| 226 |
+
|
| 227 |
+
**Output When No Changes**
|
| 228 |
+
|
| 229 |
+
```
|
| 230 |
+
## No Changes to Archive
|
| 231 |
+
|
| 232 |
+
No active changes found. Create a new change to get started.
|
| 233 |
+
```
|
| 234 |
+
|
| 235 |
+
**Guardrails**
|
| 236 |
+
- Allow any number of changes (1+ is fine, 2+ is the typical use case)
|
| 237 |
+
- Always prompt for selection, never auto-select
|
| 238 |
+
- Detect spec conflicts early and resolve by checking codebase
|
| 239 |
+
- When both changes are implemented, apply specs in chronological order
|
| 240 |
+
- Skip spec sync only when implementation is missing (warn user)
|
| 241 |
+
- Show clear per-change status before confirming
|
| 242 |
+
- Use single confirmation for entire batch
|
| 243 |
+
- Track and report all outcomes (success/skip/fail)
|
| 244 |
+
- Preserve .openspec.yaml when moving to archive
|
| 245 |
+
- Archive directory target uses current date: YYYY-MM-DD-<name>
|
| 246 |
+
- If archive target exists, fail that change but continue with others
|
.github/skills/openspec-continue-change/SKILL.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-continue-change
|
| 3 |
+
description: Continue working on an OpenSpec change by creating the next artifact. Use when the user wants to progress their change, create the next artifact, or continue their workflow.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Continue working on a change by creating the next artifact.
|
| 13 |
+
|
| 14 |
+
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 15 |
+
|
| 16 |
+
**Steps**
|
| 17 |
+
|
| 18 |
+
1. **If no change name provided, prompt for selection**
|
| 19 |
+
|
| 20 |
+
Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to work on.
|
| 21 |
+
|
| 22 |
+
Present the top 3-4 most recently modified changes as options, showing:
|
| 23 |
+
- Change name
|
| 24 |
+
- Schema (from `schema` field if present, otherwise "spec-driven")
|
| 25 |
+
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
| 26 |
+
- How recently it was modified (from `lastModified` field)
|
| 27 |
+
|
| 28 |
+
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to continue.
|
| 29 |
+
|
| 30 |
+
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
| 31 |
+
|
| 32 |
+
2. **Check current status**
|
| 33 |
+
```bash
|
| 34 |
+
openspec status --change "<name>" --json
|
| 35 |
+
```
|
| 36 |
+
Parse the JSON to understand current state. The response includes:
|
| 37 |
+
- `schemaName`: The workflow schema being used (e.g., "spec-driven")
|
| 38 |
+
- `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
|
| 39 |
+
- `isComplete`: Boolean indicating if all artifacts are complete
|
| 40 |
+
|
| 41 |
+
3. **Act based on status**:
|
| 42 |
+
|
| 43 |
+
---
|
| 44 |
+
|
| 45 |
+
**If all artifacts are complete (`isComplete: true`)**:
|
| 46 |
+
- Congratulate the user
|
| 47 |
+
- Show final status including the schema used
|
| 48 |
+
- Suggest: "All artifacts created! You can now implement this change or archive it."
|
| 49 |
+
- STOP
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
+
**If artifacts are ready to create** (status shows artifacts with `status: "ready"`):
|
| 54 |
+
- Pick the FIRST artifact with `status: "ready"` from the status output
|
| 55 |
+
- Get its instructions:
|
| 56 |
+
```bash
|
| 57 |
+
openspec instructions <artifact-id> --change "<name>" --json
|
| 58 |
+
```
|
| 59 |
+
- Parse the JSON. The key fields are:
|
| 60 |
+
- `context`: Project background (constraints for you - do NOT include in output)
|
| 61 |
+
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
| 62 |
+
- `template`: The structure to use for your output file
|
| 63 |
+
- `instruction`: Schema-specific guidance
|
| 64 |
+
- `outputPath`: Where to write the artifact
|
| 65 |
+
- `dependencies`: Completed artifacts to read for context
|
| 66 |
+
- **Create the artifact file**:
|
| 67 |
+
- Read any completed dependency files for context
|
| 68 |
+
- Use `template` as the structure - fill in its sections
|
| 69 |
+
- Apply `context` and `rules` as constraints when writing - but do NOT copy them into the file
|
| 70 |
+
- Write to the output path specified in instructions
|
| 71 |
+
- Show what was created and what's now unlocked
|
| 72 |
+
- STOP after creating ONE artifact
|
| 73 |
+
|
| 74 |
+
---
|
| 75 |
+
|
| 76 |
+
**If no artifacts are ready (all blocked)**:
|
| 77 |
+
- This shouldn't happen with a valid schema
|
| 78 |
+
- Show status and suggest checking for issues
|
| 79 |
+
|
| 80 |
+
4. **After creating an artifact, show progress**
|
| 81 |
+
```bash
|
| 82 |
+
openspec status --change "<name>"
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
**Output**
|
| 86 |
+
|
| 87 |
+
After each invocation, show:
|
| 88 |
+
- Which artifact was created
|
| 89 |
+
- Schema workflow being used
|
| 90 |
+
- Current progress (N/M complete)
|
| 91 |
+
- What artifacts are now unlocked
|
| 92 |
+
- Prompt: "Want to continue? Just ask me to continue or tell me what to do next."
|
| 93 |
+
|
| 94 |
+
**Artifact Creation Guidelines**
|
| 95 |
+
|
| 96 |
+
The artifact types and their purpose depend on the schema. Use the `instruction` field from the instructions output to understand what to create.
|
| 97 |
+
|
| 98 |
+
Common artifact patterns:
|
| 99 |
+
|
| 100 |
+
**spec-driven schema** (proposal → specs → design → tasks):
|
| 101 |
+
- **proposal.md**: Ask user about the change if not clear. Fill in Why, What Changes, Capabilities, Impact.
|
| 102 |
+
- The Capabilities section is critical - each capability listed will need a spec file.
|
| 103 |
+
- **specs/<capability>/spec.md**: Create one spec per capability listed in the proposal's Capabilities section (use the capability name, not the change name).
|
| 104 |
+
- **design.md**: Document technical decisions, architecture, and implementation approach.
|
| 105 |
+
- **tasks.md**: Break down implementation into checkboxed tasks.
|
| 106 |
+
|
| 107 |
+
For other schemas, follow the `instruction` field from the CLI output.
|
| 108 |
+
|
| 109 |
+
**Guardrails**
|
| 110 |
+
- Create ONE artifact per invocation
|
| 111 |
+
- Always read dependency artifacts before creating a new one
|
| 112 |
+
- Never skip artifacts or create out of order
|
| 113 |
+
- If context is unclear, ask the user before creating
|
| 114 |
+
- Verify the artifact file exists after writing before marking progress
|
| 115 |
+
- Use the schema's artifact sequence, don't assume specific artifact names
|
| 116 |
+
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
| 117 |
+
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
| 118 |
+
- These guide what you write, but should never appear in the output
|
.github/skills/openspec-explore/SKILL.md
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-explore
|
| 3 |
+
description: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
| 13 |
+
|
| 14 |
+
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
| 15 |
+
|
| 16 |
+
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
| 17 |
+
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
## The Stance
|
| 21 |
+
|
| 22 |
+
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
| 23 |
+
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
| 24 |
+
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
| 25 |
+
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
| 26 |
+
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
| 27 |
+
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
## What You Might Do
|
| 32 |
+
|
| 33 |
+
Depending on what the user brings, you might:
|
| 34 |
+
|
| 35 |
+
**Explore the problem space**
|
| 36 |
+
- Ask clarifying questions that emerge from what they said
|
| 37 |
+
- Challenge assumptions
|
| 38 |
+
- Reframe the problem
|
| 39 |
+
- Find analogies
|
| 40 |
+
|
| 41 |
+
**Investigate the codebase**
|
| 42 |
+
- Map existing architecture relevant to the discussion
|
| 43 |
+
- Find integration points
|
| 44 |
+
- Identify patterns already in use
|
| 45 |
+
- Surface hidden complexity
|
| 46 |
+
|
| 47 |
+
**Compare options**
|
| 48 |
+
- Brainstorm multiple approaches
|
| 49 |
+
- Build comparison tables
|
| 50 |
+
- Sketch tradeoffs
|
| 51 |
+
- Recommend a path (if asked)
|
| 52 |
+
|
| 53 |
+
**Visualize**
|
| 54 |
+
```
|
| 55 |
+
┌─────────────────────────────────────────┐
|
| 56 |
+
│ Use ASCII diagrams liberally │
|
| 57 |
+
├─────────────────────────────────────────┤
|
| 58 |
+
│ │
|
| 59 |
+
│ ┌────────┐ ┌────────┐ │
|
| 60 |
+
│ │ State │────────▶│ State │ │
|
| 61 |
+
│ │ A │ │ B │ │
|
| 62 |
+
│ └────────┘ └────────┘ │
|
| 63 |
+
│ │
|
| 64 |
+
│ System diagrams, state machines, │
|
| 65 |
+
│ data flows, architecture sketches, │
|
| 66 |
+
│ dependency graphs, comparison tables │
|
| 67 |
+
│ │
|
| 68 |
+
└─────────────────────────────────────────┘
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
**Surface risks and unknowns**
|
| 72 |
+
- Identify what could go wrong
|
| 73 |
+
- Find gaps in understanding
|
| 74 |
+
- Suggest spikes or investigations
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
+
## OpenSpec Awareness
|
| 79 |
+
|
| 80 |
+
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
| 81 |
+
|
| 82 |
+
### Check for context
|
| 83 |
+
|
| 84 |
+
At the start, quickly check what exists:
|
| 85 |
+
```bash
|
| 86 |
+
openspec list --json
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
This tells you:
|
| 90 |
+
- If there are active changes
|
| 91 |
+
- Their names, schemas, and status
|
| 92 |
+
- What the user might be working on
|
| 93 |
+
|
| 94 |
+
### When no change exists
|
| 95 |
+
|
| 96 |
+
Think freely. When insights crystallize, you might offer:
|
| 97 |
+
|
| 98 |
+
- "This feels solid enough to start a change. Want me to create a proposal?"
|
| 99 |
+
- Or keep exploring - no pressure to formalize
|
| 100 |
+
|
| 101 |
+
### When a change exists
|
| 102 |
+
|
| 103 |
+
If the user mentions a change or you detect one is relevant:
|
| 104 |
+
|
| 105 |
+
1. **Read existing artifacts for context**
|
| 106 |
+
- `openspec/changes/<name>/proposal.md`
|
| 107 |
+
- `openspec/changes/<name>/design.md`
|
| 108 |
+
- `openspec/changes/<name>/tasks.md`
|
| 109 |
+
- etc.
|
| 110 |
+
|
| 111 |
+
2. **Reference them naturally in conversation**
|
| 112 |
+
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
| 113 |
+
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
| 114 |
+
|
| 115 |
+
3. **Offer to capture when decisions are made**
|
| 116 |
+
|
| 117 |
+
| Insight Type | Where to Capture |
|
| 118 |
+
|--------------|------------------|
|
| 119 |
+
| New requirement discovered | `specs/<capability>/spec.md` |
|
| 120 |
+
| Requirement changed | `specs/<capability>/spec.md` |
|
| 121 |
+
| Design decision made | `design.md` |
|
| 122 |
+
| Scope changed | `proposal.md` |
|
| 123 |
+
| New work identified | `tasks.md` |
|
| 124 |
+
| Assumption invalidated | Relevant artifact |
|
| 125 |
+
|
| 126 |
+
Example offers:
|
| 127 |
+
- "That's a design decision. Capture it in design.md?"
|
| 128 |
+
- "This is a new requirement. Add it to specs?"
|
| 129 |
+
- "This changes scope. Update the proposal?"
|
| 130 |
+
|
| 131 |
+
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
| 132 |
+
|
| 133 |
+
---
|
| 134 |
+
|
| 135 |
+
## What You Don't Have To Do
|
| 136 |
+
|
| 137 |
+
- Follow a script
|
| 138 |
+
- Ask the same questions every time
|
| 139 |
+
- Produce a specific artifact
|
| 140 |
+
- Reach a conclusion
|
| 141 |
+
- Stay on topic if a tangent is valuable
|
| 142 |
+
- Be brief (this is thinking time)
|
| 143 |
+
|
| 144 |
+
---
|
| 145 |
+
|
| 146 |
+
## Handling Different Entry Points
|
| 147 |
+
|
| 148 |
+
**User brings a vague idea:**
|
| 149 |
+
```
|
| 150 |
+
User: I'm thinking about adding real-time collaboration
|
| 151 |
+
|
| 152 |
+
You: Real-time collab is a big space. Let me think about this...
|
| 153 |
+
|
| 154 |
+
COLLABORATION SPECTRUM
|
| 155 |
+
════════════════════════════════════════════
|
| 156 |
+
|
| 157 |
+
Awareness Coordination Sync
|
| 158 |
+
│ │ │
|
| 159 |
+
▼ ▼ ▼
|
| 160 |
+
┌────────┐ ┌────────┐ ┌────────┐
|
| 161 |
+
│Presence│ │Cursors │ │ CRDT │
|
| 162 |
+
│ "3 │ │ Multi │ │Conflict│
|
| 163 |
+
│online" │ │ select │ │ free │
|
| 164 |
+
└────────┘ └────────┘ └────────┘
|
| 165 |
+
│ │ │
|
| 166 |
+
trivial moderate complex
|
| 167 |
+
|
| 168 |
+
Where's your head at?
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
**User brings a specific problem:**
|
| 172 |
+
```
|
| 173 |
+
User: The auth system is a mess
|
| 174 |
+
|
| 175 |
+
You: [reads codebase]
|
| 176 |
+
|
| 177 |
+
┌─────────────────────────────────────────────┐
|
| 178 |
+
│ CURRENT AUTH FLOW │
|
| 179 |
+
└─────────────────────────────────────────────┘
|
| 180 |
+
│
|
| 181 |
+
┌─────────────┼─────────────┐
|
| 182 |
+
▼ ▼ ▼
|
| 183 |
+
┌─────────┐ ┌─────────┐ ┌─────────┐
|
| 184 |
+
│ Google │ │ GitHub │ │ Email │
|
| 185 |
+
│ OAuth │ │ OAuth │ │ Magic │
|
| 186 |
+
└────┬────┘ └────┬────┘ └────┬────┘
|
| 187 |
+
│ │ │
|
| 188 |
+
└─────────────┼─────────────┘
|
| 189 |
+
▼
|
| 190 |
+
┌───────────┐
|
| 191 |
+
│ Session │
|
| 192 |
+
└─────┬─────┘
|
| 193 |
+
│
|
| 194 |
+
▼
|
| 195 |
+
┌───────────┐
|
| 196 |
+
│ Perms │
|
| 197 |
+
└───────────┘
|
| 198 |
+
|
| 199 |
+
I see three tangles. Which one's burning?
|
| 200 |
+
```
|
| 201 |
+
|
| 202 |
+
**User is stuck mid-implementation:**
|
| 203 |
+
```
|
| 204 |
+
User: /opsx:explore add-auth-system
|
| 205 |
+
The OAuth integration is more complex than expected
|
| 206 |
+
|
| 207 |
+
You: [reads change artifacts]
|
| 208 |
+
|
| 209 |
+
You're on task 4: "Implement OAuth flow"
|
| 210 |
+
|
| 211 |
+
Let me trace what's involved...
|
| 212 |
+
|
| 213 |
+
[draws diagram, explores options, suggests paths]
|
| 214 |
+
|
| 215 |
+
Want to update the design to reflect this?
|
| 216 |
+
Or add a spike task to investigate?
|
| 217 |
+
```
|
| 218 |
+
|
| 219 |
+
**User wants to compare options:**
|
| 220 |
+
```
|
| 221 |
+
User: Should we use Postgres or SQLite?
|
| 222 |
+
|
| 223 |
+
You: Generic answer is boring. What's the context?
|
| 224 |
+
|
| 225 |
+
User: A CLI tool that tracks local dev environments
|
| 226 |
+
|
| 227 |
+
You: That changes everything.
|
| 228 |
+
|
| 229 |
+
┌─────────────────────────────────────────────────┐
|
| 230 |
+
│ CLI TOOL DATA STORAGE │
|
| 231 |
+
└─────────────────────────────────────────────────┘
|
| 232 |
+
|
| 233 |
+
Key constraints:
|
| 234 |
+
• No daemon running
|
| 235 |
+
• Must work offline
|
| 236 |
+
• Single user
|
| 237 |
+
|
| 238 |
+
SQLite Postgres
|
| 239 |
+
Deployment embedded ✓ needs server ✗
|
| 240 |
+
Offline yes ✓ no ✗
|
| 241 |
+
Single file yes ✓ no ✗
|
| 242 |
+
|
| 243 |
+
SQLite. Not even close.
|
| 244 |
+
|
| 245 |
+
Unless... is there a sync component?
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
---
|
| 249 |
+
|
| 250 |
+
## Ending Discovery
|
| 251 |
+
|
| 252 |
+
There's no required ending. Discovery might:
|
| 253 |
+
|
| 254 |
+
- **Flow into a proposal**: "Ready to start? I can create a change proposal."
|
| 255 |
+
- **Result in artifact updates**: "Updated design.md with these decisions"
|
| 256 |
+
- **Just provide clarity**: User has what they need, moves on
|
| 257 |
+
- **Continue later**: "We can pick this up anytime"
|
| 258 |
+
|
| 259 |
+
When it feels like things are crystallizing, you might summarize:
|
| 260 |
+
|
| 261 |
+
```
|
| 262 |
+
## What We Figured Out
|
| 263 |
+
|
| 264 |
+
**The problem**: [crystallized understanding]
|
| 265 |
+
|
| 266 |
+
**The approach**: [if one emerged]
|
| 267 |
+
|
| 268 |
+
**Open questions**: [if any remain]
|
| 269 |
+
|
| 270 |
+
**Next steps** (if ready):
|
| 271 |
+
- Create a change proposal
|
| 272 |
+
- Keep exploring: just keep talking
|
| 273 |
+
```
|
| 274 |
+
|
| 275 |
+
But this summary is optional. Sometimes the thinking IS the value.
|
| 276 |
+
|
| 277 |
+
---
|
| 278 |
+
|
| 279 |
+
## Guardrails
|
| 280 |
+
|
| 281 |
+
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
|
| 282 |
+
- **Don't fake understanding** - If something is unclear, dig deeper
|
| 283 |
+
- **Don't rush** - Discovery is thinking time, not task time
|
| 284 |
+
- **Don't force structure** - Let patterns emerge naturally
|
| 285 |
+
- **Don't auto-capture** - Offer to save insights, don't just do it
|
| 286 |
+
- **Do visualize** - A good diagram is worth many paragraphs
|
| 287 |
+
- **Do explore the codebase** - Ground discussions in reality
|
| 288 |
+
- **Do question assumptions** - Including the user's and your own
|
.github/skills/openspec-ff-change/SKILL.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-ff-change
|
| 3 |
+
description: Fast-forward through OpenSpec artifact creation. Use when the user wants to quickly create all artifacts needed for implementation without stepping through each one individually.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Fast-forward through artifact creation - generate everything needed to start implementation in one go.
|
| 13 |
+
|
| 14 |
+
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
| 15 |
+
|
| 16 |
+
**Steps**
|
| 17 |
+
|
| 18 |
+
1. **If no clear input provided, ask what they want to build**
|
| 19 |
+
|
| 20 |
+
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
| 21 |
+
> "What change do you want to work on? Describe what you want to build or fix."
|
| 22 |
+
|
| 23 |
+
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
| 24 |
+
|
| 25 |
+
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
| 26 |
+
|
| 27 |
+
2. **Create the change directory**
|
| 28 |
+
```bash
|
| 29 |
+
openspec new change "<name>"
|
| 30 |
+
```
|
| 31 |
+
This creates a scaffolded change at `openspec/changes/<name>/`.
|
| 32 |
+
|
| 33 |
+
3. **Get the artifact build order**
|
| 34 |
+
```bash
|
| 35 |
+
openspec status --change "<name>" --json
|
| 36 |
+
```
|
| 37 |
+
Parse the JSON to get:
|
| 38 |
+
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
| 39 |
+
- `artifacts`: list of all artifacts with their status and dependencies
|
| 40 |
+
|
| 41 |
+
4. **Create artifacts in sequence until apply-ready**
|
| 42 |
+
|
| 43 |
+
Use the **TodoWrite tool** to track progress through the artifacts.
|
| 44 |
+
|
| 45 |
+
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
| 46 |
+
|
| 47 |
+
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
| 48 |
+
- Get instructions:
|
| 49 |
+
```bash
|
| 50 |
+
openspec instructions <artifact-id> --change "<name>" --json
|
| 51 |
+
```
|
| 52 |
+
- The instructions JSON includes:
|
| 53 |
+
- `context`: Project background (constraints for you - do NOT include in output)
|
| 54 |
+
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
| 55 |
+
- `template`: The structure to use for your output file
|
| 56 |
+
- `instruction`: Schema-specific guidance for this artifact type
|
| 57 |
+
- `outputPath`: Where to write the artifact
|
| 58 |
+
- `dependencies`: Completed artifacts to read for context
|
| 59 |
+
- Read any completed dependency files for context
|
| 60 |
+
- Create the artifact file using `template` as the structure
|
| 61 |
+
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
| 62 |
+
- Show brief progress: "✓ Created <artifact-id>"
|
| 63 |
+
|
| 64 |
+
b. **Continue until all `applyRequires` artifacts are complete**
|
| 65 |
+
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
| 66 |
+
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
| 67 |
+
- Stop when all `applyRequires` artifacts are done
|
| 68 |
+
|
| 69 |
+
c. **If an artifact requires user input** (unclear context):
|
| 70 |
+
- Use **AskUserQuestion tool** to clarify
|
| 71 |
+
- Then continue with creation
|
| 72 |
+
|
| 73 |
+
5. **Show final status**
|
| 74 |
+
```bash
|
| 75 |
+
openspec status --change "<name>"
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
**Output**
|
| 79 |
+
|
| 80 |
+
After completing all artifacts, summarize:
|
| 81 |
+
- Change name and location
|
| 82 |
+
- List of artifacts created with brief descriptions
|
| 83 |
+
- What's ready: "All artifacts created! Ready for implementation."
|
| 84 |
+
- Prompt: "Run `/opsx:apply` or ask me to implement to start working on the tasks."
|
| 85 |
+
|
| 86 |
+
**Artifact Creation Guidelines**
|
| 87 |
+
|
| 88 |
+
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
| 89 |
+
- The schema defines what each artifact should contain - follow it
|
| 90 |
+
- Read dependency artifacts for context before creating new ones
|
| 91 |
+
- Use `template` as the structure for your output file - fill in its sections
|
| 92 |
+
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
| 93 |
+
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
| 94 |
+
- These guide what you write, but should never appear in the output
|
| 95 |
+
|
| 96 |
+
**Guardrails**
|
| 97 |
+
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
| 98 |
+
- Always read dependency artifacts before creating a new one
|
| 99 |
+
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
| 100 |
+
- If a change with that name already exists, suggest continuing that change instead
|
| 101 |
+
- Verify each artifact file exists after writing before proceeding to next
|
.github/skills/openspec-new-change/SKILL.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-new-change
|
| 3 |
+
description: Start a new OpenSpec change using the experimental artifact workflow. Use when the user wants to create a new feature, fix, or modification with a structured step-by-step approach.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Start a new change using the experimental artifact-driven approach.
|
| 13 |
+
|
| 14 |
+
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
| 15 |
+
|
| 16 |
+
**Steps**
|
| 17 |
+
|
| 18 |
+
1. **If no clear input provided, ask what they want to build**
|
| 19 |
+
|
| 20 |
+
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
| 21 |
+
> "What change do you want to work on? Describe what you want to build or fix."
|
| 22 |
+
|
| 23 |
+
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
| 24 |
+
|
| 25 |
+
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
| 26 |
+
|
| 27 |
+
2. **Determine the workflow schema**
|
| 28 |
+
|
| 29 |
+
Use the default schema (omit `--schema`) unless the user explicitly requests a different workflow.
|
| 30 |
+
|
| 31 |
+
**Use a different schema only if the user mentions:**
|
| 32 |
+
- A specific schema name → use `--schema <name>`
|
| 33 |
+
- "show workflows" or "what workflows" → run `openspec schemas --json` and let them choose
|
| 34 |
+
|
| 35 |
+
**Otherwise**: Omit `--schema` to use the default.
|
| 36 |
+
|
| 37 |
+
3. **Create the change directory**
|
| 38 |
+
```bash
|
| 39 |
+
openspec new change "<name>"
|
| 40 |
+
```
|
| 41 |
+
Add `--schema <name>` only if the user requested a specific workflow.
|
| 42 |
+
This creates a scaffolded change at `openspec/changes/<name>/` with the selected schema.
|
| 43 |
+
|
| 44 |
+
4. **Show the artifact status**
|
| 45 |
+
```bash
|
| 46 |
+
openspec status --change "<name>"
|
| 47 |
+
```
|
| 48 |
+
This shows which artifacts need to be created and which are ready (dependencies satisfied).
|
| 49 |
+
|
| 50 |
+
5. **Get instructions for the first artifact**
|
| 51 |
+
The first artifact depends on the schema (e.g., `proposal` for spec-driven).
|
| 52 |
+
Check the status output to find the first artifact with status "ready".
|
| 53 |
+
```bash
|
| 54 |
+
openspec instructions <first-artifact-id> --change "<name>"
|
| 55 |
+
```
|
| 56 |
+
This outputs the template and context for creating the first artifact.
|
| 57 |
+
|
| 58 |
+
6. **STOP and wait for user direction**
|
| 59 |
+
|
| 60 |
+
**Output**
|
| 61 |
+
|
| 62 |
+
After completing the steps, summarize:
|
| 63 |
+
- Change name and location
|
| 64 |
+
- Schema/workflow being used and its artifact sequence
|
| 65 |
+
- Current status (0/N artifacts complete)
|
| 66 |
+
- The template for the first artifact
|
| 67 |
+
- Prompt: "Ready to create the first artifact? Just describe what this change is about and I'll draft it, or ask me to continue."
|
| 68 |
+
|
| 69 |
+
**Guardrails**
|
| 70 |
+
- Do NOT create any artifacts yet - just show the instructions
|
| 71 |
+
- Do NOT advance beyond showing the first artifact template
|
| 72 |
+
- If the name is invalid (not kebab-case), ask for a valid name
|
| 73 |
+
- If a change with that name already exists, suggest continuing that change instead
|
| 74 |
+
- Pass --schema if using a non-default workflow
|
.github/skills/openspec-onboard/SKILL.md
ADDED
|
@@ -0,0 +1,554 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-onboard
|
| 3 |
+
description: Guided onboarding for OpenSpec - walk through a complete workflow cycle with narration and real codebase work.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Guide the user through their first complete OpenSpec workflow cycle. This is a teaching experience—you'll do real work in their codebase while explaining each step.
|
| 13 |
+
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
## Preflight
|
| 17 |
+
|
| 18 |
+
Before starting, check if the OpenSpec CLI is installed:
|
| 19 |
+
|
| 20 |
+
```bash
|
| 21 |
+
# Unix/macOS
|
| 22 |
+
openspec --version 2>&1 || echo "CLI_NOT_INSTALLED"
|
| 23 |
+
# Windows (PowerShell)
|
| 24 |
+
# if (Get-Command openspec -ErrorAction SilentlyContinue) { openspec --version } else { echo "CLI_NOT_INSTALLED" }
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
**If CLI not installed:**
|
| 28 |
+
> OpenSpec CLI is not installed. Install it first, then come back to `/opsx:onboard`.
|
| 29 |
+
|
| 30 |
+
Stop here if not installed.
|
| 31 |
+
|
| 32 |
+
---
|
| 33 |
+
|
| 34 |
+
## Phase 1: Welcome
|
| 35 |
+
|
| 36 |
+
Display:
|
| 37 |
+
|
| 38 |
+
```
|
| 39 |
+
## Welcome to OpenSpec!
|
| 40 |
+
|
| 41 |
+
I'll walk you through a complete change cycle—from idea to implementation—using a real task in your codebase. Along the way, you'll learn the workflow by doing it.
|
| 42 |
+
|
| 43 |
+
**What we'll do:**
|
| 44 |
+
1. Pick a small, real task in your codebase
|
| 45 |
+
2. Explore the problem briefly
|
| 46 |
+
3. Create a change (the container for our work)
|
| 47 |
+
4. Build the artifacts: proposal → specs → design → tasks
|
| 48 |
+
5. Implement the tasks
|
| 49 |
+
6. Archive the completed change
|
| 50 |
+
|
| 51 |
+
**Time:** ~15-20 minutes
|
| 52 |
+
|
| 53 |
+
Let's start by finding something to work on.
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
---
|
| 57 |
+
|
| 58 |
+
## Phase 2: Task Selection
|
| 59 |
+
|
| 60 |
+
### Codebase Analysis
|
| 61 |
+
|
| 62 |
+
Scan the codebase for small improvement opportunities. Look for:
|
| 63 |
+
|
| 64 |
+
1. **TODO/FIXME comments** - Search for `TODO`, `FIXME`, `HACK`, `XXX` in code files
|
| 65 |
+
2. **Missing error handling** - `catch` blocks that swallow errors, risky operations without try-catch
|
| 66 |
+
3. **Functions without tests** - Cross-reference `src/` with test directories
|
| 67 |
+
4. **Type issues** - `any` types in TypeScript files (`: any`, `as any`)
|
| 68 |
+
5. **Debug artifacts** - `console.log`, `console.debug`, `debugger` statements in non-debug code
|
| 69 |
+
6. **Missing validation** - User input handlers without validation
|
| 70 |
+
|
| 71 |
+
Also check recent git activity:
|
| 72 |
+
```bash
|
| 73 |
+
# Unix/macOS
|
| 74 |
+
git log --oneline -10 2>/dev/null || echo "No git history"
|
| 75 |
+
# Windows (PowerShell)
|
| 76 |
+
# git log --oneline -10 2>$null; if ($LASTEXITCODE -ne 0) { echo "No git history" }
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
### Present Suggestions
|
| 80 |
+
|
| 81 |
+
From your analysis, present 3-4 specific suggestions:
|
| 82 |
+
|
| 83 |
+
```
|
| 84 |
+
## Task Suggestions
|
| 85 |
+
|
| 86 |
+
Based on scanning your codebase, here are some good starter tasks:
|
| 87 |
+
|
| 88 |
+
**1. [Most promising task]**
|
| 89 |
+
Location: `src/path/to/file.ts:42`
|
| 90 |
+
Scope: ~1-2 files, ~20-30 lines
|
| 91 |
+
Why it's good: [brief reason]
|
| 92 |
+
|
| 93 |
+
**2. [Second task]**
|
| 94 |
+
Location: `src/another/file.ts`
|
| 95 |
+
Scope: ~1 file, ~15 lines
|
| 96 |
+
Why it's good: [brief reason]
|
| 97 |
+
|
| 98 |
+
**3. [Third task]**
|
| 99 |
+
Location: [location]
|
| 100 |
+
Scope: [estimate]
|
| 101 |
+
Why it's good: [brief reason]
|
| 102 |
+
|
| 103 |
+
**4. Something else?**
|
| 104 |
+
Tell me what you'd like to work on.
|
| 105 |
+
|
| 106 |
+
Which task interests you? (Pick a number or describe your own)
|
| 107 |
+
```
|
| 108 |
+
|
| 109 |
+
**If nothing found:** Fall back to asking what the user wants to build:
|
| 110 |
+
> I didn't find obvious quick wins in your codebase. What's something small you've been meaning to add or fix?
|
| 111 |
+
|
| 112 |
+
### Scope Guardrail
|
| 113 |
+
|
| 114 |
+
If the user picks or describes something too large (major feature, multi-day work):
|
| 115 |
+
|
| 116 |
+
```
|
| 117 |
+
That's a valuable task, but it's probably larger than ideal for your first OpenSpec run-through.
|
| 118 |
+
|
| 119 |
+
For learning the workflow, smaller is better—it lets you see the full cycle without getting stuck in implementation details.
|
| 120 |
+
|
| 121 |
+
**Options:**
|
| 122 |
+
1. **Slice it smaller** - What's the smallest useful piece of [their task]? Maybe just [specific slice]?
|
| 123 |
+
2. **Pick something else** - One of the other suggestions, or a different small task?
|
| 124 |
+
3. **Do it anyway** - If you really want to tackle this, we can. Just know it'll take longer.
|
| 125 |
+
|
| 126 |
+
What would you prefer?
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
Let the user override if they insist—this is a soft guardrail.
|
| 130 |
+
|
| 131 |
+
---
|
| 132 |
+
|
| 133 |
+
## Phase 3: Explore Demo
|
| 134 |
+
|
| 135 |
+
Once a task is selected, briefly demonstrate explore mode:
|
| 136 |
+
|
| 137 |
+
```
|
| 138 |
+
Before we create a change, let me quickly show you **explore mode**—it's how you think through problems before committing to a direction.
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
Spend 1-2 minutes investigating the relevant code:
|
| 142 |
+
- Read the file(s) involved
|
| 143 |
+
- Draw a quick ASCII diagram if it helps
|
| 144 |
+
- Note any considerations
|
| 145 |
+
|
| 146 |
+
```
|
| 147 |
+
## Quick Exploration
|
| 148 |
+
|
| 149 |
+
[Your brief analysis—what you found, any considerations]
|
| 150 |
+
|
| 151 |
+
┌─────────────────────────────────────────┐
|
| 152 |
+
│ [Optional: ASCII diagram if helpful] │
|
| 153 |
+
└─────────────────────────────────────────┘
|
| 154 |
+
|
| 155 |
+
Explore mode (`/opsx:explore`) is for this kind of thinking—investigating before implementing. You can use it anytime you need to think through a problem.
|
| 156 |
+
|
| 157 |
+
Now let's create a change to hold our work.
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
**PAUSE** - Wait for user acknowledgment before proceeding.
|
| 161 |
+
|
| 162 |
+
---
|
| 163 |
+
|
| 164 |
+
## Phase 4: Create the Change
|
| 165 |
+
|
| 166 |
+
**EXPLAIN:**
|
| 167 |
+
```
|
| 168 |
+
## Creating a Change
|
| 169 |
+
|
| 170 |
+
A "change" in OpenSpec is a container for all the thinking and planning around a piece of work. It lives in `openspec/changes/<name>/` and holds your artifacts—proposal, specs, design, tasks.
|
| 171 |
+
|
| 172 |
+
Let me create one for our task.
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
**DO:** Create the change with a derived kebab-case name:
|
| 176 |
+
```bash
|
| 177 |
+
openspec new change "<derived-name>"
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
**SHOW:**
|
| 181 |
+
```
|
| 182 |
+
Created: `openspec/changes/<name>/`
|
| 183 |
+
|
| 184 |
+
The folder structure:
|
| 185 |
+
```
|
| 186 |
+
openspec/changes/<name>/
|
| 187 |
+
├── proposal.md ← Why we're doing this (empty, we'll fill it)
|
| 188 |
+
├── design.md ← How we'll build it (empty)
|
| 189 |
+
├── specs/ ← Detailed requirements (empty)
|
| 190 |
+
└── tasks.md ← Implementation checklist (empty)
|
| 191 |
+
```
|
| 192 |
+
|
| 193 |
+
Now let's fill in the first artifact—the proposal.
|
| 194 |
+
```
|
| 195 |
+
|
| 196 |
+
---
|
| 197 |
+
|
| 198 |
+
## Phase 5: Proposal
|
| 199 |
+
|
| 200 |
+
**EXPLAIN:**
|
| 201 |
+
```
|
| 202 |
+
## The Proposal
|
| 203 |
+
|
| 204 |
+
The proposal captures **why** we're making this change and **what** it involves at a high level. It's the "elevator pitch" for the work.
|
| 205 |
+
|
| 206 |
+
I'll draft one based on our task.
|
| 207 |
+
```
|
| 208 |
+
|
| 209 |
+
**DO:** Draft the proposal content (don't save yet):
|
| 210 |
+
|
| 211 |
+
```
|
| 212 |
+
Here's a draft proposal:
|
| 213 |
+
|
| 214 |
+
---
|
| 215 |
+
|
| 216 |
+
## Why
|
| 217 |
+
|
| 218 |
+
[1-2 sentences explaining the problem/opportunity]
|
| 219 |
+
|
| 220 |
+
## What Changes
|
| 221 |
+
|
| 222 |
+
[Bullet points of what will be different]
|
| 223 |
+
|
| 224 |
+
## Capabilities
|
| 225 |
+
|
| 226 |
+
### New Capabilities
|
| 227 |
+
- `<capability-name>`: [brief description]
|
| 228 |
+
|
| 229 |
+
### Modified Capabilities
|
| 230 |
+
<!-- If modifying existing behavior -->
|
| 231 |
+
|
| 232 |
+
## Impact
|
| 233 |
+
|
| 234 |
+
- `src/path/to/file.ts`: [what changes]
|
| 235 |
+
- [other files if applicable]
|
| 236 |
+
|
| 237 |
+
---
|
| 238 |
+
|
| 239 |
+
Does this capture the intent? I can adjust before we save it.
|
| 240 |
+
```
|
| 241 |
+
|
| 242 |
+
**PAUSE** - Wait for user approval/feedback.
|
| 243 |
+
|
| 244 |
+
After approval, save the proposal:
|
| 245 |
+
```bash
|
| 246 |
+
openspec instructions proposal --change "<name>" --json
|
| 247 |
+
```
|
| 248 |
+
Then write the content to `openspec/changes/<name>/proposal.md`.
|
| 249 |
+
|
| 250 |
+
```
|
| 251 |
+
Proposal saved. This is your "why" document—you can always come back and refine it as understanding evolves.
|
| 252 |
+
|
| 253 |
+
Next up: specs.
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
---
|
| 257 |
+
|
| 258 |
+
## Phase 6: Specs
|
| 259 |
+
|
| 260 |
+
**EXPLAIN:**
|
| 261 |
+
```
|
| 262 |
+
## Specs
|
| 263 |
+
|
| 264 |
+
Specs define **what** we're building in precise, testable terms. They use a requirement/scenario format that makes expected behavior crystal clear.
|
| 265 |
+
|
| 266 |
+
For a small task like this, we might only need one spec file.
|
| 267 |
+
```
|
| 268 |
+
|
| 269 |
+
**DO:** Create the spec file:
|
| 270 |
+
```bash
|
| 271 |
+
# Unix/macOS
|
| 272 |
+
mkdir -p openspec/changes/<name>/specs/<capability-name>
|
| 273 |
+
# Windows (PowerShell)
|
| 274 |
+
# New-Item -ItemType Directory -Force -Path "openspec/changes/<name>/specs/<capability-name>"
|
| 275 |
+
```
|
| 276 |
+
|
| 277 |
+
Draft the spec content:
|
| 278 |
+
|
| 279 |
+
```
|
| 280 |
+
Here's the spec:
|
| 281 |
+
|
| 282 |
+
---
|
| 283 |
+
|
| 284 |
+
## ADDED Requirements
|
| 285 |
+
|
| 286 |
+
### Requirement: <Name>
|
| 287 |
+
|
| 288 |
+
<Description of what the system should do>
|
| 289 |
+
|
| 290 |
+
#### Scenario: <Scenario name>
|
| 291 |
+
|
| 292 |
+
- **WHEN** <trigger condition>
|
| 293 |
+
- **THEN** <expected outcome>
|
| 294 |
+
- **AND** <additional outcome if needed>
|
| 295 |
+
|
| 296 |
+
---
|
| 297 |
+
|
| 298 |
+
This format—WHEN/THEN/AND—makes requirements testable. You can literally read them as test cases.
|
| 299 |
+
```
|
| 300 |
+
|
| 301 |
+
Save to `openspec/changes/<name>/specs/<capability>/spec.md`.
|
| 302 |
+
|
| 303 |
+
---
|
| 304 |
+
|
| 305 |
+
## Phase 7: Design
|
| 306 |
+
|
| 307 |
+
**EXPLAIN:**
|
| 308 |
+
```
|
| 309 |
+
## Design
|
| 310 |
+
|
| 311 |
+
The design captures **how** we'll build it—technical decisions, tradeoffs, approach.
|
| 312 |
+
|
| 313 |
+
For small changes, this might be brief. That's fine—not every change needs deep design discussion.
|
| 314 |
+
```
|
| 315 |
+
|
| 316 |
+
**DO:** Draft design.md:
|
| 317 |
+
|
| 318 |
+
```
|
| 319 |
+
Here's the design:
|
| 320 |
+
|
| 321 |
+
---
|
| 322 |
+
|
| 323 |
+
## Context
|
| 324 |
+
|
| 325 |
+
[Brief context about the current state]
|
| 326 |
+
|
| 327 |
+
## Goals / Non-Goals
|
| 328 |
+
|
| 329 |
+
**Goals:**
|
| 330 |
+
- [What we're trying to achieve]
|
| 331 |
+
|
| 332 |
+
**Non-Goals:**
|
| 333 |
+
- [What's explicitly out of scope]
|
| 334 |
+
|
| 335 |
+
## Decisions
|
| 336 |
+
|
| 337 |
+
### Decision 1: [Key decision]
|
| 338 |
+
|
| 339 |
+
[Explanation of approach and rationale]
|
| 340 |
+
|
| 341 |
+
---
|
| 342 |
+
|
| 343 |
+
For a small task, this captures the key decisions without over-engineering.
|
| 344 |
+
```
|
| 345 |
+
|
| 346 |
+
Save to `openspec/changes/<name>/design.md`.
|
| 347 |
+
|
| 348 |
+
---
|
| 349 |
+
|
| 350 |
+
## Phase 8: Tasks
|
| 351 |
+
|
| 352 |
+
**EXPLAIN:**
|
| 353 |
+
```
|
| 354 |
+
## Tasks
|
| 355 |
+
|
| 356 |
+
Finally, we break the work into implementation tasks—checkboxes that drive the apply phase.
|
| 357 |
+
|
| 358 |
+
These should be small, clear, and in logical order.
|
| 359 |
+
```
|
| 360 |
+
|
| 361 |
+
**DO:** Generate tasks based on specs and design:
|
| 362 |
+
|
| 363 |
+
```
|
| 364 |
+
Here are the implementation tasks:
|
| 365 |
+
|
| 366 |
+
---
|
| 367 |
+
|
| 368 |
+
## 1. [Category or file]
|
| 369 |
+
|
| 370 |
+
- [ ] 1.1 [Specific task]
|
| 371 |
+
- [ ] 1.2 [Specific task]
|
| 372 |
+
|
| 373 |
+
## 2. Verify
|
| 374 |
+
|
| 375 |
+
- [ ] 2.1 [Verification step]
|
| 376 |
+
|
| 377 |
+
---
|
| 378 |
+
|
| 379 |
+
Each checkbox becomes a unit of work in the apply phase. Ready to implement?
|
| 380 |
+
```
|
| 381 |
+
|
| 382 |
+
**PAUSE** - Wait for user to confirm they're ready to implement.
|
| 383 |
+
|
| 384 |
+
Save to `openspec/changes/<name>/tasks.md`.
|
| 385 |
+
|
| 386 |
+
---
|
| 387 |
+
|
| 388 |
+
## Phase 9: Apply (Implementation)
|
| 389 |
+
|
| 390 |
+
**EXPLAIN:**
|
| 391 |
+
```
|
| 392 |
+
## Implementation
|
| 393 |
+
|
| 394 |
+
Now we implement each task, checking them off as we go. I'll announce each one and occasionally note how the specs/design informed the approach.
|
| 395 |
+
```
|
| 396 |
+
|
| 397 |
+
**DO:** For each task:
|
| 398 |
+
|
| 399 |
+
1. Announce: "Working on task N: [description]"
|
| 400 |
+
2. Implement the change in the codebase
|
| 401 |
+
3. Reference specs/design naturally: "The spec says X, so I'm doing Y"
|
| 402 |
+
4. Mark complete in tasks.md: `- [ ]` → `- [x]`
|
| 403 |
+
5. Brief status: "✓ Task N complete"
|
| 404 |
+
|
| 405 |
+
Keep narration light—don't over-explain every line of code.
|
| 406 |
+
|
| 407 |
+
After all tasks:
|
| 408 |
+
|
| 409 |
+
```
|
| 410 |
+
## Implementation Complete
|
| 411 |
+
|
| 412 |
+
All tasks done:
|
| 413 |
+
- [x] Task 1
|
| 414 |
+
- [x] Task 2
|
| 415 |
+
- [x] ...
|
| 416 |
+
|
| 417 |
+
The change is implemented! One more step—let's archive it.
|
| 418 |
+
```
|
| 419 |
+
|
| 420 |
+
---
|
| 421 |
+
|
| 422 |
+
## Phase 10: Archive
|
| 423 |
+
|
| 424 |
+
**EXPLAIN:**
|
| 425 |
+
```
|
| 426 |
+
## Archiving
|
| 427 |
+
|
| 428 |
+
When a change is complete, we archive it. This moves it from `openspec/changes/` to `openspec/changes/archive/YYYY-MM-DD-<name>/`.
|
| 429 |
+
|
| 430 |
+
Archived changes become your project's decision history—you can always find them later to understand why something was built a certain way.
|
| 431 |
+
```
|
| 432 |
+
|
| 433 |
+
**DO:**
|
| 434 |
+
```bash
|
| 435 |
+
openspec archive "<name>"
|
| 436 |
+
```
|
| 437 |
+
|
| 438 |
+
**SHOW:**
|
| 439 |
+
```
|
| 440 |
+
Archived to: `openspec/changes/archive/YYYY-MM-DD-<name>/`
|
| 441 |
+
|
| 442 |
+
The change is now part of your project's history. The code is in your codebase, the decision record is preserved.
|
| 443 |
+
```
|
| 444 |
+
|
| 445 |
+
---
|
| 446 |
+
|
| 447 |
+
## Phase 11: Recap & Next Steps
|
| 448 |
+
|
| 449 |
+
```
|
| 450 |
+
## Congratulations!
|
| 451 |
+
|
| 452 |
+
You just completed a full OpenSpec cycle:
|
| 453 |
+
|
| 454 |
+
1. **Explore** - Thought through the problem
|
| 455 |
+
2. **New** - Created a change container
|
| 456 |
+
3. **Proposal** - Captured WHY
|
| 457 |
+
4. **Specs** - Defined WHAT in detail
|
| 458 |
+
5. **Design** - Decided HOW
|
| 459 |
+
6. **Tasks** - Broke it into steps
|
| 460 |
+
7. **Apply** - Implemented the work
|
| 461 |
+
8. **Archive** - Preserved the record
|
| 462 |
+
|
| 463 |
+
This same rhythm works for any size change—a small fix or a major feature.
|
| 464 |
+
|
| 465 |
+
---
|
| 466 |
+
|
| 467 |
+
## Command Reference
|
| 468 |
+
|
| 469 |
+
**Core workflow:**
|
| 470 |
+
|
| 471 |
+
| Command | What it does |
|
| 472 |
+
|---------|--------------|
|
| 473 |
+
| `/opsx:propose` | Create a change and generate all artifacts |
|
| 474 |
+
| `/opsx:explore` | Think through problems before/during work |
|
| 475 |
+
| `/opsx:apply` | Implement tasks from a change |
|
| 476 |
+
| `/opsx:archive` | Archive a completed change |
|
| 477 |
+
|
| 478 |
+
**Additional commands:**
|
| 479 |
+
|
| 480 |
+
| Command | What it does |
|
| 481 |
+
|---------|--------------|
|
| 482 |
+
| `/opsx:new` | Start a new change, step through artifacts one at a time |
|
| 483 |
+
| `/opsx:continue` | Continue working on an existing change |
|
| 484 |
+
| `/opsx:ff` | Fast-forward: create all artifacts at once |
|
| 485 |
+
| `/opsx:verify` | Verify implementation matches artifacts |
|
| 486 |
+
|
| 487 |
+
---
|
| 488 |
+
|
| 489 |
+
## What's Next?
|
| 490 |
+
|
| 491 |
+
Try `/opsx:propose` on something you actually want to build. You've got the rhythm now!
|
| 492 |
+
```
|
| 493 |
+
|
| 494 |
+
---
|
| 495 |
+
|
| 496 |
+
## Graceful Exit Handling
|
| 497 |
+
|
| 498 |
+
### User wants to stop mid-way
|
| 499 |
+
|
| 500 |
+
If the user says they need to stop, want to pause, or seem disengaged:
|
| 501 |
+
|
| 502 |
+
```
|
| 503 |
+
No problem! Your change is saved at `openspec/changes/<name>/`.
|
| 504 |
+
|
| 505 |
+
To pick up where we left off later:
|
| 506 |
+
- `/opsx:continue <name>` - Resume artifact creation
|
| 507 |
+
- `/opsx:apply <name>` - Jump to implementation (if tasks exist)
|
| 508 |
+
|
| 509 |
+
The work won't be lost. Come back whenever you're ready.
|
| 510 |
+
```
|
| 511 |
+
|
| 512 |
+
Exit gracefully without pressure.
|
| 513 |
+
|
| 514 |
+
### User just wants command reference
|
| 515 |
+
|
| 516 |
+
If the user says they just want to see the commands or skip the tutorial:
|
| 517 |
+
|
| 518 |
+
```
|
| 519 |
+
## OpenSpec Quick Reference
|
| 520 |
+
|
| 521 |
+
**Core workflow:**
|
| 522 |
+
|
| 523 |
+
| Command | What it does |
|
| 524 |
+
|---------|--------------|
|
| 525 |
+
| `/opsx:propose <name>` | Create a change and generate all artifacts |
|
| 526 |
+
| `/opsx:explore` | Think through problems (no code changes) |
|
| 527 |
+
| `/opsx:apply <name>` | Implement tasks |
|
| 528 |
+
| `/opsx:archive <name>` | Archive when done |
|
| 529 |
+
|
| 530 |
+
**Additional commands:**
|
| 531 |
+
|
| 532 |
+
| Command | What it does |
|
| 533 |
+
|---------|--------------|
|
| 534 |
+
| `/opsx:new <name>` | Start a new change, step by step |
|
| 535 |
+
| `/opsx:continue <name>` | Continue an existing change |
|
| 536 |
+
| `/opsx:ff <name>` | Fast-forward: all artifacts at once |
|
| 537 |
+
| `/opsx:verify <name>` | Verify implementation |
|
| 538 |
+
|
| 539 |
+
Try `/opsx:propose` to start your first change.
|
| 540 |
+
```
|
| 541 |
+
|
| 542 |
+
Exit gracefully.
|
| 543 |
+
|
| 544 |
+
---
|
| 545 |
+
|
| 546 |
+
## Guardrails
|
| 547 |
+
|
| 548 |
+
- **Follow the EXPLAIN → DO → SHOW → PAUSE pattern** at key transitions (after explore, after proposal draft, after tasks, after archive)
|
| 549 |
+
- **Keep narration light** during implementation—teach without lecturing
|
| 550 |
+
- **Don't skip phases** even if the change is small—the goal is teaching the workflow
|
| 551 |
+
- **Pause for acknowledgment** at marked points, but don't over-pause
|
| 552 |
+
- **Handle exits gracefully**—never pressure the user to continue
|
| 553 |
+
- **Use real codebase tasks**—don't simulate or use fake examples
|
| 554 |
+
- **Adjust scope gently**—guide toward smaller tasks but respect user choice
|
.github/skills/openspec-sync-specs/SKILL.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-sync-specs
|
| 3 |
+
description: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Sync delta specs from a change to main specs.
|
| 13 |
+
|
| 14 |
+
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
| 15 |
+
|
| 16 |
+
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 17 |
+
|
| 18 |
+
**Steps**
|
| 19 |
+
|
| 20 |
+
1. **If no change name provided, prompt for selection**
|
| 21 |
+
|
| 22 |
+
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
| 23 |
+
|
| 24 |
+
Show changes that have delta specs (under `specs/` directory).
|
| 25 |
+
|
| 26 |
+
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
| 27 |
+
|
| 28 |
+
2. **Find delta specs**
|
| 29 |
+
|
| 30 |
+
Look for delta spec files in `openspec/changes/<name>/specs/*/spec.md`.
|
| 31 |
+
|
| 32 |
+
Each delta spec file contains sections like:
|
| 33 |
+
- `## ADDED Requirements` - New requirements to add
|
| 34 |
+
- `## MODIFIED Requirements` - Changes to existing requirements
|
| 35 |
+
- `## REMOVED Requirements` - Requirements to remove
|
| 36 |
+
- `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
|
| 37 |
+
|
| 38 |
+
If no delta specs found, inform user and stop.
|
| 39 |
+
|
| 40 |
+
3. **For each delta spec, apply changes to main specs**
|
| 41 |
+
|
| 42 |
+
For each capability with a delta spec at `openspec/changes/<name>/specs/<capability>/spec.md`:
|
| 43 |
+
|
| 44 |
+
a. **Read the delta spec** to understand the intended changes
|
| 45 |
+
|
| 46 |
+
b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
|
| 47 |
+
|
| 48 |
+
c. **Apply changes intelligently**:
|
| 49 |
+
|
| 50 |
+
**ADDED Requirements:**
|
| 51 |
+
- If requirement doesn't exist in main spec → add it
|
| 52 |
+
- If requirement already exists → update it to match (treat as implicit MODIFIED)
|
| 53 |
+
|
| 54 |
+
**MODIFIED Requirements:**
|
| 55 |
+
- Find the requirement in main spec
|
| 56 |
+
- Apply the changes - this can be:
|
| 57 |
+
- Adding new scenarios (don't need to copy existing ones)
|
| 58 |
+
- Modifying existing scenarios
|
| 59 |
+
- Changing the requirement description
|
| 60 |
+
- Preserve scenarios/content not mentioned in the delta
|
| 61 |
+
|
| 62 |
+
**REMOVED Requirements:**
|
| 63 |
+
- Remove the entire requirement block from main spec
|
| 64 |
+
|
| 65 |
+
**RENAMED Requirements:**
|
| 66 |
+
- Find the FROM requirement, rename to TO
|
| 67 |
+
|
| 68 |
+
d. **Create new main spec** if capability doesn't exist yet:
|
| 69 |
+
- Create `openspec/specs/<capability>/spec.md`
|
| 70 |
+
- Add Purpose section (can be brief, mark as TBD)
|
| 71 |
+
- Add Requirements section with the ADDED requirements
|
| 72 |
+
|
| 73 |
+
4. **Show summary**
|
| 74 |
+
|
| 75 |
+
After applying all changes, summarize:
|
| 76 |
+
- Which capabilities were updated
|
| 77 |
+
- What changes were made (requirements added/modified/removed/renamed)
|
| 78 |
+
|
| 79 |
+
**Delta Spec Format Reference**
|
| 80 |
+
|
| 81 |
+
```markdown
|
| 82 |
+
## ADDED Requirements
|
| 83 |
+
|
| 84 |
+
### Requirement: New Feature
|
| 85 |
+
The system SHALL do something new.
|
| 86 |
+
|
| 87 |
+
#### Scenario: Basic case
|
| 88 |
+
- **WHEN** user does X
|
| 89 |
+
- **THEN** system does Y
|
| 90 |
+
|
| 91 |
+
## MODIFIED Requirements
|
| 92 |
+
|
| 93 |
+
### Requirement: Existing Feature
|
| 94 |
+
#### Scenario: New scenario to add
|
| 95 |
+
- **WHEN** user does A
|
| 96 |
+
- **THEN** system does B
|
| 97 |
+
|
| 98 |
+
## REMOVED Requirements
|
| 99 |
+
|
| 100 |
+
### Requirement: Deprecated Feature
|
| 101 |
+
|
| 102 |
+
## RENAMED Requirements
|
| 103 |
+
|
| 104 |
+
- FROM: `### Requirement: Old Name`
|
| 105 |
+
- TO: `### Requirement: New Name`
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
**Key Principle: Intelligent Merging**
|
| 109 |
+
|
| 110 |
+
Unlike programmatic merging, you can apply **partial updates**:
|
| 111 |
+
- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
|
| 112 |
+
- The delta represents *intent*, not a wholesale replacement
|
| 113 |
+
- Use your judgment to merge changes sensibly
|
| 114 |
+
|
| 115 |
+
**Output On Success**
|
| 116 |
+
|
| 117 |
+
```
|
| 118 |
+
## Specs Synced: <change-name>
|
| 119 |
+
|
| 120 |
+
Updated main specs:
|
| 121 |
+
|
| 122 |
+
**<capability-1>**:
|
| 123 |
+
- Added requirement: "New Feature"
|
| 124 |
+
- Modified requirement: "Existing Feature" (added 1 scenario)
|
| 125 |
+
|
| 126 |
+
**<capability-2>**:
|
| 127 |
+
- Created new spec file
|
| 128 |
+
- Added requirement: "Another Feature"
|
| 129 |
+
|
| 130 |
+
Main specs are now updated. The change remains active - archive when implementation is complete.
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
**Guardrails**
|
| 134 |
+
- Read both delta and main specs before making changes
|
| 135 |
+
- Preserve existing content not mentioned in delta
|
| 136 |
+
- If something is unclear, ask for clarification
|
| 137 |
+
- Show what you're changing as you go
|
| 138 |
+
- The operation should be idempotent - running twice should give same result
|
.github/skills/openspec-verify-change/SKILL.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: openspec-verify-change
|
| 3 |
+
description: Verify implementation matches change artifacts. Use when the user wants to validate that implementation is complete, correct, and coherent before archiving.
|
| 4 |
+
license: MIT
|
| 5 |
+
compatibility: Requires openspec CLI.
|
| 6 |
+
metadata:
|
| 7 |
+
author: openspec
|
| 8 |
+
version: "1.0"
|
| 9 |
+
generatedBy: "1.2.0"
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Verify that an implementation matches the change artifacts (specs, tasks, design).
|
| 13 |
+
|
| 14 |
+
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
| 15 |
+
|
| 16 |
+
**Steps**
|
| 17 |
+
|
| 18 |
+
1. **If no change name provided, prompt for selection**
|
| 19 |
+
|
| 20 |
+
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
| 21 |
+
|
| 22 |
+
Show changes that have implementation tasks (tasks artifact exists).
|
| 23 |
+
Include the schema used for each change if available.
|
| 24 |
+
Mark changes with incomplete tasks as "(In Progress)".
|
| 25 |
+
|
| 26 |
+
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
| 27 |
+
|
| 28 |
+
2. **Check status to understand the schema**
|
| 29 |
+
```bash
|
| 30 |
+
openspec status --change "<name>" --json
|
| 31 |
+
```
|
| 32 |
+
Parse the JSON to understand:
|
| 33 |
+
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
| 34 |
+
- Which artifacts exist for this change
|
| 35 |
+
|
| 36 |
+
3. **Get the change directory and load artifacts**
|
| 37 |
+
|
| 38 |
+
```bash
|
| 39 |
+
openspec instructions apply --change "<name>" --json
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
This returns the change directory and context files. Read all available artifacts from `contextFiles`.
|
| 43 |
+
|
| 44 |
+
4. **Initialize verification report structure**
|
| 45 |
+
|
| 46 |
+
Create a report structure with three dimensions:
|
| 47 |
+
- **Completeness**: Track tasks and spec coverage
|
| 48 |
+
- **Correctness**: Track requirement implementation and scenario coverage
|
| 49 |
+
- **Coherence**: Track design adherence and pattern consistency
|
| 50 |
+
|
| 51 |
+
Each dimension can have CRITICAL, WARNING, or SUGGESTION issues.
|
| 52 |
+
|
| 53 |
+
5. **Verify Completeness**
|
| 54 |
+
|
| 55 |
+
**Task Completion**:
|
| 56 |
+
- If tasks.md exists in contextFiles, read it
|
| 57 |
+
- Parse checkboxes: `- [ ]` (incomplete) vs `- [x]` (complete)
|
| 58 |
+
- Count complete vs total tasks
|
| 59 |
+
- If incomplete tasks exist:
|
| 60 |
+
- Add CRITICAL issue for each incomplete task
|
| 61 |
+
- Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
|
| 62 |
+
|
| 63 |
+
**Spec Coverage**:
|
| 64 |
+
- If delta specs exist in `openspec/changes/<name>/specs/`:
|
| 65 |
+
- Extract all requirements (marked with "### Requirement:")
|
| 66 |
+
- For each requirement:
|
| 67 |
+
- Search codebase for keywords related to the requirement
|
| 68 |
+
- Assess if implementation likely exists
|
| 69 |
+
- If requirements appear unimplemented:
|
| 70 |
+
- Add CRITICAL issue: "Requirement not found: <requirement name>"
|
| 71 |
+
- Recommendation: "Implement requirement X: <description>"
|
| 72 |
+
|
| 73 |
+
6. **Verify Correctness**
|
| 74 |
+
|
| 75 |
+
**Requirement Implementation Mapping**:
|
| 76 |
+
- For each requirement from delta specs:
|
| 77 |
+
- Search codebase for implementation evidence
|
| 78 |
+
- If found, note file paths and line ranges
|
| 79 |
+
- Assess if implementation matches requirement intent
|
| 80 |
+
- If divergence detected:
|
| 81 |
+
- Add WARNING: "Implementation may diverge from spec: <details>"
|
| 82 |
+
- Recommendation: "Review <file>:<lines> against requirement X"
|
| 83 |
+
|
| 84 |
+
**Scenario Coverage**:
|
| 85 |
+
- For each scenario in delta specs (marked with "#### Scenario:"):
|
| 86 |
+
- Check if conditions are handled in code
|
| 87 |
+
- Check if tests exist covering the scenario
|
| 88 |
+
- If scenario appears uncovered:
|
| 89 |
+
- Add WARNING: "Scenario not covered: <scenario name>"
|
| 90 |
+
- Recommendation: "Add test or implementation for scenario: <description>"
|
| 91 |
+
|
| 92 |
+
7. **Verify Coherence**
|
| 93 |
+
|
| 94 |
+
**Design Adherence**:
|
| 95 |
+
- If design.md exists in contextFiles:
|
| 96 |
+
- Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
|
| 97 |
+
- Verify implementation follows those decisions
|
| 98 |
+
- If contradiction detected:
|
| 99 |
+
- Add WARNING: "Design decision not followed: <decision>"
|
| 100 |
+
- Recommendation: "Update implementation or revise design.md to match reality"
|
| 101 |
+
- If no design.md: Skip design adherence check, note "No design.md to verify against"
|
| 102 |
+
|
| 103 |
+
**Code Pattern Consistency**:
|
| 104 |
+
- Review new code for consistency with project patterns
|
| 105 |
+
- Check file naming, directory structure, coding style
|
| 106 |
+
- If significant deviations found:
|
| 107 |
+
- Add SUGGESTION: "Code pattern deviation: <details>"
|
| 108 |
+
- Recommendation: "Consider following project pattern: <example>"
|
| 109 |
+
|
| 110 |
+
8. **Generate Verification Report**
|
| 111 |
+
|
| 112 |
+
**Summary Scorecard**:
|
| 113 |
+
```
|
| 114 |
+
## Verification Report: <change-name>
|
| 115 |
+
|
| 116 |
+
### Summary
|
| 117 |
+
| Dimension | Status |
|
| 118 |
+
|--------------|------------------|
|
| 119 |
+
| Completeness | X/Y tasks, N reqs|
|
| 120 |
+
| Correctness | M/N reqs covered |
|
| 121 |
+
| Coherence | Followed/Issues |
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
**Issues by Priority**:
|
| 125 |
+
|
| 126 |
+
1. **CRITICAL** (Must fix before archive):
|
| 127 |
+
- Incomplete tasks
|
| 128 |
+
- Missing requirement implementations
|
| 129 |
+
- Each with specific, actionable recommendation
|
| 130 |
+
|
| 131 |
+
2. **WARNING** (Should fix):
|
| 132 |
+
- Spec/design divergences
|
| 133 |
+
- Missing scenario coverage
|
| 134 |
+
- Each with specific recommendation
|
| 135 |
+
|
| 136 |
+
3. **SUGGESTION** (Nice to fix):
|
| 137 |
+
- Pattern inconsistencies
|
| 138 |
+
- Minor improvements
|
| 139 |
+
- Each with specific recommendation
|
| 140 |
+
|
| 141 |
+
**Final Assessment**:
|
| 142 |
+
- If CRITICAL issues: "X critical issue(s) found. Fix before archiving."
|
| 143 |
+
- If only warnings: "No critical issues. Y warning(s) to consider. Ready for archive (with noted improvements)."
|
| 144 |
+
- If all clear: "All checks passed. Ready for archive."
|
| 145 |
+
|
| 146 |
+
**Verification Heuristics**
|
| 147 |
+
|
| 148 |
+
- **Completeness**: Focus on objective checklist items (checkboxes, requirements list)
|
| 149 |
+
- **Correctness**: Use keyword search, file path analysis, reasonable inference - don't require perfect certainty
|
| 150 |
+
- **Coherence**: Look for glaring inconsistencies, don't nitpick style
|
| 151 |
+
- **False Positives**: When uncertain, prefer SUGGESTION over WARNING, WARNING over CRITICAL
|
| 152 |
+
- **Actionability**: Every issue must have a specific recommendation with file/line references where applicable
|
| 153 |
+
|
| 154 |
+
**Graceful Degradation**
|
| 155 |
+
|
| 156 |
+
- If only tasks.md exists: verify task completion only, skip spec/design checks
|
| 157 |
+
- If tasks + specs exist: verify completeness and correctness, skip design
|
| 158 |
+
- If full artifacts: verify all three dimensions
|
| 159 |
+
- Always note which checks were skipped and why
|
| 160 |
+
|
| 161 |
+
**Output Format**
|
| 162 |
+
|
| 163 |
+
Use clear markdown with:
|
| 164 |
+
- Table for summary scorecard
|
| 165 |
+
- Grouped lists for issues (CRITICAL/WARNING/SUGGESTION)
|
| 166 |
+
- Code references in format: `file.ts:123`
|
| 167 |
+
- Specific, actionable recommendations
|
| 168 |
+
- No vague suggestions like "consider reviewing"
|
.vscode/mcp.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"servers": {}
|
| 3 |
+
}
|
.vscode/settings.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"terminal.integrated.env.windows": {
|
| 3 |
+
"AZURE_DEVOPS_PAT": "9iNB6I1NnwsCtciwIZcHEeOGH9s3ESLiY5ZbhZrlQ84Jac7x5MH1JQQJ99CAACAAAAAr9WPjAAASAZDO1ZqO"
|
| 4 |
+
},
|
| 5 |
+
"chat.useClaudeSkills": true,
|
| 6 |
+
"chat.useAgentSkills": true
|
| 7 |
+
}
|
ChatMemo.txt
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
�ؿ��U���{���Ohuggin### ? �s�W�妸���R�\��G
|
| 2 |
+
- ? �s�W�������ɭ��A�O�d���Ѥ��R�\��
|
| 3 |
+
- ? **������r��J��**�G�����b�s��������J�h�ӪѲ��N��
|
| 4 |
+
- ? **������j�ѪR**�G�䴩����B�r���B�����B�Ů浥�h�ؤ��j�覡
|
| 5 |
+
- ? **�ֳt�d�ҫ��s**�G�x�Ѽ����B���Ѭ�ޡB����ETF�@���J
|
| 6 |
+
- ? �����T�Y����ܡG
|
| 7 |
+
- �Ѳ��N���B�Ѳ��W�١B���e����
|
| 8 |
+
- �W�����v(%)�B�U�^���v(%)�B�L����v(%)
|
| 9 |
+
- �H�߫�(%)�B���R�ɶ�
|
| 10 |
+
- ������~�B�z�M���A��ܡA�ثe�i�H��J�Ѳ��N���A���Ѥ��R�Ầ�^��ij�A�O�i�H�אּ�ѳ]�w��StockList.txt�A�@���ʬd�߲M�椺���Ѳ��A�ç�G(��%�B�^%�B�L%�A�H��%)�A�g�JStockResult.csv�A�Х�hugging face�M�ק����A�A�i�H�ϰݧڰ��D�H��M�ݨD�C
|
| 11 |
+
|
| 12 |
+
1.�ݭn��L��T�H�p�Ѳ��W�١B���e����B���R�ɶ�
|
| 13 |
+
2.�b Web �ɭ����s�W�@�ӧ妸���R���\��
|
| 14 |
+
3.�b���G������~
|
| 15 |
+
4.���@���ʤ��R�A���ڦ����IJ�o�����s
|
| 16 |
+
|
| 17 |
+
�нվ�妸���R���G�A��G�ιϪ����覡��ܦb�e���W
|
| 18 |
+
�������R������|���� StockResult.csv �ɮסA��G�����ιϪ���ܦb�����W��
|
| 19 |
+
�妸���R�i�_�קאּ�Ѻ�����textfield��J�h�����e�A���N�ثe��StockList.txtŪ�J���覡�O?
|
| 20 |
+
|
| 21 |
+
## ? �w�����\�� (app_batch.py) - �t�Ϫ���ı��
|
| 22 |
+
|
| 23 |
+
### ? �s�W�妸���R�\��G
|
| 24 |
+
- ? �s�W�������ɭ��A�O�d���Ѥ��R�\��
|
| 25 |
+
- ? Ū�� StockList.txt �i��妸���R
|
| 26 |
+
- ? ��X�����T�� StockResult.csv�G
|
| 27 |
+
- �Ѳ��N���B�Ѳ��W�١B���e����
|
| 28 |
+
- �W�����v(%)�B�U�^���v(%)�B�L����v(%)
|
| 29 |
+
- �H�߫�(%)�B���R�ɶ�
|
| 30 |
+
- ���~�T���]������~�B�z�^
|
| 31 |
+
- ? ���IJ�o���s�u? �}�l�妸���R�v
|
| 32 |
+
- ? �Y�ɶi����ܩM���G�έp
|
| 33 |
+
- ? ���~�B�z�G�ƾڤ����B�������~�B�N�����~��
|
| 34 |
+
|
| 35 |
+
### ? �s�W�Ϫ���ı�ƥ\��G
|
| 36 |
+
- ? **���v����W����**�G�U�Ѳ��W��/�U�^/�L����v���
|
| 37 |
+
- ? **�H�߫״��G��**�G�w���H�߫פ��G�A���j�p���̰ܳ����v
|
| 38 |
+
- ? **��X�����p�F��**�G�h���תѲ���������]�e6��Ѳ��^
|
| 39 |
+
- ? **�����������**�G����ݦh/�ݪ�/�L��Ѳ��ƶq�έp
|
| 40 |
+
- ? **�Y�ɤ��ʹϪ�**�G�䴩��j�B�Y��B�a����ܸԲӸ�T
|
| 41 |
+
- ? **�Բӵ��G����**�G����ƾڪ���A�t�C��s�X�M�w����V
|
| 42 |
+
|
| 43 |
+
### ? ��X�榡�G
|
| 44 |
+
�����Y�ɹϪ� + ���ʦ����G����A�L�ݤU���ɮ�
|
| 45 |
+
|
| 46 |
+
### ? �ϥΤ覡�G
|
| 47 |
+
1. �T�O StockList.txt �s�b�]�C��@�ӪѲ��N���^
|
| 48 |
+
2. �B�� app_batch.py
|
| 49 |
+
3. �}���s�����i�J Web �ɭ�
|
| 50 |
+
4. �I��u? �妸�Ѳ����R�v����
|
| 51 |
+
5. �I���u? �}�l�妸���R�v���s
|
| 52 |
+
6. **�Y�ɬd��5�ص�ı�Ƶ��G**�G
|
| 53 |
+
- ? ���v����W����
|
| 54 |
+
- ? �H�߫״��G��
|
| 55 |
+
- ? ��X�����p�F��
|
| 56 |
+
- ? �����������
|
| 57 |
+
- ? �Բӵ��G����]�t�C��s�X�^
|
| 58 |
+
|
| 59 |
+
### ? �Ϫ��\����G
|
| 60 |
+
- **���v�����**�G���[����U�Ѳ����^�L���v
|
| 61 |
+
- **�H�߫פ��G**�G���V�j���ܹw�����v�V���A�C��N����V
|
| 62 |
+
- **�p�F��**�G�h�������A�A�X�D���u���
|
| 63 |
+
- **�������**�G���饫���h�Ť�Ҥ@�ؤF�M
|
| 64 |
+
|
| 65 |
+
### ? ���M�����U�G
|
| 66 |
+
- ���Ѳ��GAI�w���ݦh
|
| 67 |
+
- ����Ѳ��GAI�w���ݪ�
|
| 68 |
+
- �Ǧ�Ѳ��GAI�w���L��
|
| 69 |
+
- ���j�p�G�w���j��
|
| 70 |
+
- �H�߫סG�w���i�a��
|
| 71 |
+
|
| 72 |
+
## ? �̷s��s�G������r��J�\��
|
| 73 |
+
|
| 74 |
+
### ? �s�\��S��G
|
| 75 |
+
- ? **���� StockList.txt �ɮר̿�**�G���A�ݭn�dzƥ~���ɮ�
|
| 76 |
+
- ? **����������J**�G�b�j����r�ؤ�������J�h�ӪѲ��N��
|
| 77 |
+
- ? **������j�ѪR**�G�۰��ѧO�h�ؤ��j�š]����B�r���B�����B�Ů�^
|
| 78 |
+
- ? **�ֳt�d�ҫ��s**�G
|
| 79 |
+
- ?? �x�Ѽ����G�x�n�q�B�E���B�p�o��B������B�x�F�q�B�p�q
|
| 80 |
+
- ?? ���Ѭ�ޡGAAPL�BMSFT�BGOOGL�BTSLA�BNVDA�BAMZN
|
| 81 |
+
- ? ����ETF�G0050�B0056�BVTI�BVOO�BQQQ�BSPY
|
| 82 |
+
- ? **�M�ū��s**�G�@��M�ſ�J���e���s�}�l
|
| 83 |
+
|
| 84 |
+
### ? ��J�d�ҡG
|
| 85 |
+
```
|
| 86 |
+
�覡1 - ������j�G
|
| 87 |
+
2330.TW
|
| 88 |
+
2317.TW
|
| 89 |
+
1303.TW
|
| 90 |
+
|
| 91 |
+
�覡2 - �r�����j�G
|
| 92 |
+
2330.TW, 2317.TW, 1303.TW
|
| 93 |
+
|
| 94 |
+
�覡3 - �V�X���j�G
|
| 95 |
+
2330.TW, 2317.TW
|
| 96 |
+
1303.TW; AAPL TSLA
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
### ? �ϥ����紣�ɡG
|
| 100 |
+
- ? **���[**�G�Ҧ��ާ@���b�����W����
|
| 101 |
+
- ? **��ֳt**�G�I���d�ҫ��s�ߧY��J�����Ѳ�
|
| 102 |
+
- ? **���F��**�G�䴩���N�զX�����j�Ÿ�
|
| 103 |
+
- ? **��͵�**�G�M������J���ܩM�d�һ���
|
DARK_MODE_GUIDE.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 深色模式使用指南
|
| 2 |
+
|
| 3 |
+
## 概述
|
| 4 |
+
|
| 5 |
+
StockRecommender 現已支援深色模式,為用戶提供在低光環境中更舒適的使用體驗。
|
| 6 |
+
|
| 7 |
+
## 如何使用深色模式
|
| 8 |
+
|
| 9 |
+
### 切換主題
|
| 10 |
+
|
| 11 |
+
1. **點擊主題切換按鈕**:在應用程式標題欄右側,您會看到一個主題切換按鈕
|
| 12 |
+
- 亮色模式:顯示 "🌙 深色模式" 文字
|
| 13 |
+
- 深色模式:顯示 "☀️ 亮色模式" 文字
|
| 14 |
+
|
| 15 |
+
2. **鍵盤快捷方式**:按鈕支援鍵盤導航
|
| 16 |
+
- 使用 Tab 鍵導航至按鈕
|
| 17 |
+
- 按 Enter 或 Space 鍵切換主題
|
| 18 |
+
|
| 19 |
+
### 主題偏好設定持久化
|
| 20 |
+
|
| 21 |
+
- 您選擇的主題將被自動儲存到瀏覽器 localStorage
|
| 22 |
+
- 下次訪問應用程式時,系統將自動應用您的選擇
|
| 23 |
+
- 若瀏覽器不支援 localStorage,應用程式將使用預設亮色模式
|
| 24 |
+
|
| 25 |
+
### 系統偏好設定
|
| 26 |
+
|
| 27 |
+
- 首次訪問時,如果您的作業系統設定為深色模式(macOS、Windows 11、Linux 等),應用程式可自動偵測並應用深色模式
|
| 28 |
+
- 您隨時可以手動切換,覆蓋系統設定
|
| 29 |
+
|
| 30 |
+
## 支援的功能
|
| 31 |
+
|
| 32 |
+
### UI 元件
|
| 33 |
+
- ✅ Gradio 文字框、按鈕、下拉選單等所有標準元件
|
| 34 |
+
- ✅ 標題和說明文字
|
| 35 |
+
- ✅ 邊框和背景色
|
| 36 |
+
- ✅ 互動狀態(懸停、焦點)
|
| 37 |
+
|
| 38 |
+
### 圖表視覺化
|
| 39 |
+
- ✅ Plotly 長條圖
|
| 40 |
+
- ✅ Plotly 散布圖
|
| 41 |
+
- ✅ Plotly 雷達圖
|
| 42 |
+
- ✅ Plotly 圓餅圖
|
| 43 |
+
- ✅ 所有圖表文字和網格線均支援深色模式
|
| 44 |
+
|
| 45 |
+
## 無障礙性
|
| 46 |
+
|
| 47 |
+
- 所有主題切換功能都支援螢幕閱讀器(ARIA 標籤)
|
| 48 |
+
- 完全支援鍵盤導航
|
| 49 |
+
- 色彩對比度符合 WCAG AA 標準,確保易讀性
|
| 50 |
+
|
| 51 |
+
## 故障排除
|
| 52 |
+
|
| 53 |
+
### 深色模式無法切換
|
| 54 |
+
1. 清除瀏覽器快取:Ctrl+Shift+Delete(Windows)或 Cmd+Shift+Delete(Mac)
|
| 55 |
+
2. 關閉並重新打開瀏覽器
|
| 56 |
+
3. 確保 JavaScript 已啟用
|
| 57 |
+
|
| 58 |
+
### localStorage 不可用
|
| 59 |
+
- 如果瀏覽器禁用了 localStorage,應用程式會自動回退至亮色模式
|
| 60 |
+
- 您可在該會話內手動切換主題,但頁面刷新後將回到預設值
|
| 61 |
+
|
| 62 |
+
### 色彩問題
|
| 63 |
+
- 確保您的瀏覽器是最新版本
|
| 64 |
+
- 嘗試禁用瀏覽器擴展程式(特別是深色模式相關的擴展)
|
| 65 |
+
- 清除 CSS 快取:在開發者工具中禁用快取並重新加載頁面
|
| 66 |
+
|
| 67 |
+
## 技術細節
|
| 68 |
+
|
| 69 |
+
### 實現方式
|
| 70 |
+
- **CSS 變數**:使用 CSS 自定義屬性 (Custom Properties) 定義主題顏色
|
| 71 |
+
- **JavaScript 管理**:ThemeManager 類別處理主題狀態和 localStorage 持久化
|
| 72 |
+
- **Gradio 整合**:通過動態 HTML 注入和 CSS 覆蓋實現 Gradio 元件的主題支援
|
| 73 |
+
|
| 74 |
+
### 顏色方案
|
| 75 |
+
|
| 76 |
+
#### 亮色模式
|
| 77 |
+
- 背景:#ffffff(白)
|
| 78 |
+
- 文字:#212529(深灰)
|
| 79 |
+
- 主色:#1f77b4(藍)
|
| 80 |
+
- 強調色:#2ca02c(綠)
|
| 81 |
+
|
| 82 |
+
#### 深色模式
|
| 83 |
+
- 背景:#1a1a1a(深灰)
|
| 84 |
+
- 文字:#e8e8e8(淺灰)
|
| 85 |
+
- 主色:#4a9eff(亮藍)
|
| 86 |
+
- 強調色:#66dd88(亮綠)
|
| 87 |
+
|
| 88 |
+
## 回饋和建議
|
| 89 |
+
|
| 90 |
+
如果您對深色模式有任何建議或發現問題,歡迎提交 GitHub Issue 或 Pull Request。我們期待您的反饋,以持續改進使用體驗!
|
StockList.xlsx
ADDED
|
Binary file (60.2 kB). View file
|
|
|
__pycache__/app.cpython-313.pyc
ADDED
|
Binary file (86.6 kB). View file
|
|
|
app.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
app_batch.py
ADDED
|
@@ -0,0 +1,1104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 由 Copilot 生成 - AI 股票分析師 (含批次分析功能)
|
| 2 |
+
import subprocess
|
| 3 |
+
import sys
|
| 4 |
+
import os
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
|
| 7 |
+
# 環境檢測
|
| 8 |
+
IS_HUGGINGFACE_SPACE = "SPACE_ID" in os.environ
|
| 9 |
+
print(f"運行環境: {'Hugging Face Spaces' if IS_HUGGINGFACE_SPACE else '本地環境'}")
|
| 10 |
+
|
| 11 |
+
# 檢查並安裝所需套件的函數
|
| 12 |
+
def install_package(package_name):
|
| 13 |
+
try:
|
| 14 |
+
__import__(package_name)
|
| 15 |
+
except ImportError:
|
| 16 |
+
print(f"正在安裝 {package_name}...")
|
| 17 |
+
subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
|
| 18 |
+
|
| 19 |
+
# 安裝必要套件
|
| 20 |
+
required_packages = [
|
| 21 |
+
"torch>=2.0.0",
|
| 22 |
+
"torchvision>=0.15.0",
|
| 23 |
+
"torchaudio>=2.0.0",
|
| 24 |
+
"yfinance>=0.2.18",
|
| 25 |
+
"gradio>=4.0.0",
|
| 26 |
+
"pandas>=1.5.0",
|
| 27 |
+
"numpy>=1.21.0",
|
| 28 |
+
"matplotlib>=3.5.0",
|
| 29 |
+
"plotly>=5.0.0",
|
| 30 |
+
"beautifulsoup4>=4.11.0",
|
| 31 |
+
"requests>=2.28.0",
|
| 32 |
+
"transformers>=4.21.0",
|
| 33 |
+
"accelerate>=0.20.0",
|
| 34 |
+
"tokenizers>=0.13.0"
|
| 35 |
+
]
|
| 36 |
+
|
| 37 |
+
for package in required_packages:
|
| 38 |
+
package_name = package.split(">=")[0].split("==")[0]
|
| 39 |
+
if package_name == "beautifulsoup4":
|
| 40 |
+
package_name = "bs4"
|
| 41 |
+
try:
|
| 42 |
+
__import__(package_name)
|
| 43 |
+
except ImportError:
|
| 44 |
+
print(f"正在安裝 {package}...")
|
| 45 |
+
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
|
| 46 |
+
|
| 47 |
+
# 現在導入所有套件
|
| 48 |
+
import gradio as gr
|
| 49 |
+
import yfinance as yf
|
| 50 |
+
import pandas as pd
|
| 51 |
+
import numpy as np
|
| 52 |
+
import matplotlib.pyplot as plt
|
| 53 |
+
import plotly.graph_objects as go
|
| 54 |
+
import plotly.express as px
|
| 55 |
+
from datetime import datetime, timedelta
|
| 56 |
+
import requests
|
| 57 |
+
from bs4 import BeautifulSoup
|
| 58 |
+
from transformers import pipeline
|
| 59 |
+
import warnings
|
| 60 |
+
warnings.filterwarnings('ignore')
|
| 61 |
+
|
| 62 |
+
# 初始化 Hugging Face 模型
|
| 63 |
+
print("正在載入 AI 模型...")
|
| 64 |
+
|
| 65 |
+
# 嘗試載入模型,如果失敗則使用較輕量的替代方案
|
| 66 |
+
try:
|
| 67 |
+
sentiment_analyzer = pipeline("sentiment-analysis", model="ProsusAI/finbert")
|
| 68 |
+
print("FinBERT 情感分析模型載入成功")
|
| 69 |
+
except Exception as e:
|
| 70 |
+
print(f"FinBERT 載入失敗,嘗試替代模型: {e}")
|
| 71 |
+
try:
|
| 72 |
+
sentiment_analyzer = pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
|
| 73 |
+
print("多語言情感分析模型載入成功")
|
| 74 |
+
except Exception as e2:
|
| 75 |
+
print(f"替代模型載入失敗: {e2}")
|
| 76 |
+
sentiment_analyzer = None
|
| 77 |
+
|
| 78 |
+
try:
|
| 79 |
+
summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
|
| 80 |
+
print("BART 摘要模型載入成功")
|
| 81 |
+
except Exception as e:
|
| 82 |
+
print(f"BART 載入失敗,嘗試替代模型: {e}")
|
| 83 |
+
try:
|
| 84 |
+
summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6")
|
| 85 |
+
print("DistilBART 摘要模型載入成功")
|
| 86 |
+
except Exception as e2:
|
| 87 |
+
print(f"摘要模型載入失敗: {e2}")
|
| 88 |
+
summarizer = None
|
| 89 |
+
|
| 90 |
+
class StockAnalyzer:
|
| 91 |
+
def __init__(self):
|
| 92 |
+
self.data = None
|
| 93 |
+
self.symbol = None
|
| 94 |
+
|
| 95 |
+
def fetch_stock_data(self, symbol, period="1y"):
|
| 96 |
+
"""獲取股票歷史數據"""
|
| 97 |
+
try:
|
| 98 |
+
ticker = yf.Ticker(symbol)
|
| 99 |
+
self.data = ticker.history(period=period)
|
| 100 |
+
self.symbol = symbol
|
| 101 |
+
# 獲取股票資訊
|
| 102 |
+
info = ticker.info
|
| 103 |
+
stock_name = info.get('longName', info.get('shortName', symbol))
|
| 104 |
+
return True, f"成功獲取 {symbol} 的歷史數據", stock_name
|
| 105 |
+
except Exception as e:
|
| 106 |
+
return False, f"數據獲取失敗: {str(e)}", None
|
| 107 |
+
|
| 108 |
+
def get_stock_info(self, symbol):
|
| 109 |
+
"""獲取股票基本資訊"""
|
| 110 |
+
try:
|
| 111 |
+
ticker = yf.Ticker(symbol)
|
| 112 |
+
info = ticker.info
|
| 113 |
+
current_price = self.data['Close'].iloc[-1] if self.data is not None else None
|
| 114 |
+
stock_name = info.get('longName', info.get('shortName', symbol))
|
| 115 |
+
return {
|
| 116 |
+
'name': stock_name,
|
| 117 |
+
'current_price': current_price,
|
| 118 |
+
'symbol': symbol
|
| 119 |
+
}
|
| 120 |
+
except Exception as e:
|
| 121 |
+
return {
|
| 122 |
+
'name': symbol,
|
| 123 |
+
'current_price': None,
|
| 124 |
+
'symbol': symbol
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
def calculate_technical_indicators(self):
|
| 128 |
+
"""計算技術指標"""
|
| 129 |
+
if self.data is None:
|
| 130 |
+
return None
|
| 131 |
+
|
| 132 |
+
df = self.data.copy()
|
| 133 |
+
|
| 134 |
+
# 移動平均線
|
| 135 |
+
df['MA5'] = df['Close'].rolling(window=5).mean()
|
| 136 |
+
df['MA20'] = df['Close'].rolling(window=20).mean()
|
| 137 |
+
df['MA60'] = df['Close'].rolling(window=60).mean()
|
| 138 |
+
|
| 139 |
+
# RSI 相對強弱指標
|
| 140 |
+
delta = df['Close'].diff()
|
| 141 |
+
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
|
| 142 |
+
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
|
| 143 |
+
rs = gain / loss
|
| 144 |
+
df['RSI'] = 100 - (100 / (1 + rs))
|
| 145 |
+
|
| 146 |
+
# MACD
|
| 147 |
+
exp1 = df['Close'].ewm(span=12).mean()
|
| 148 |
+
exp2 = df['Close'].ewm(span=26).mean()
|
| 149 |
+
df['MACD'] = exp1 - exp2
|
| 150 |
+
df['MACD_signal'] = df['MACD'].ewm(span=9).mean()
|
| 151 |
+
|
| 152 |
+
# 布林通道
|
| 153 |
+
df['BB_middle'] = df['Close'].rolling(window=20).mean()
|
| 154 |
+
bb_std = df['Close'].rolling(window=20).std()
|
| 155 |
+
df['BB_upper'] = df['BB_middle'] + (bb_std * 2)
|
| 156 |
+
df['BB_lower'] = df['BB_middle'] - (bb_std * 2)
|
| 157 |
+
|
| 158 |
+
return df
|
| 159 |
+
|
| 160 |
+
def get_news_sentiment(self, symbol):
|
| 161 |
+
"""獲取並分析新聞情感"""
|
| 162 |
+
try:
|
| 163 |
+
# 模擬新聞標題(實際應用中需要接入新聞 API)
|
| 164 |
+
sample_news = [
|
| 165 |
+
f"{symbol} 股價創新高,投資人信心大增",
|
| 166 |
+
f"市場關注 {symbol} 最新財報表現",
|
| 167 |
+
f"{symbol} 面臨供應鏈挑戰,股價承壓",
|
| 168 |
+
f"分析師上調 {symbol} 目標價,看好後市",
|
| 169 |
+
f"{symbol} 技術創新獲得市場認可"
|
| 170 |
+
]
|
| 171 |
+
|
| 172 |
+
sentiments = []
|
| 173 |
+
|
| 174 |
+
# 檢查情感分析模型是否可用
|
| 175 |
+
if sentiment_analyzer is None:
|
| 176 |
+
# 如果模型不可用,返回模擬的情感分析結果
|
| 177 |
+
for news in sample_news:
|
| 178 |
+
# 簡單的關鍵詞情感分析替代方案
|
| 179 |
+
positive_words = ['創新高', '信心大增', '上調', '看好', '創新', '獲得認可']
|
| 180 |
+
negative_words = ['挑戰', '承壓', '面臨', '下滑']
|
| 181 |
+
|
| 182 |
+
score = 0.5 # 中性
|
| 183 |
+
sentiment = 'NEUTRAL'
|
| 184 |
+
|
| 185 |
+
for word in positive_words:
|
| 186 |
+
if word in news:
|
| 187 |
+
score = 0.8
|
| 188 |
+
sentiment = 'POSITIVE'
|
| 189 |
+
break
|
| 190 |
+
|
| 191 |
+
for word in negative_words:
|
| 192 |
+
if word in news:
|
| 193 |
+
score = 0.8
|
| 194 |
+
sentiment = 'NEGATIVE'
|
| 195 |
+
break
|
| 196 |
+
|
| 197 |
+
sentiments.append({
|
| 198 |
+
'text': news,
|
| 199 |
+
'sentiment': sentiment,
|
| 200 |
+
'score': score
|
| 201 |
+
})
|
| 202 |
+
else:
|
| 203 |
+
# 使用 AI 模型進行情感分析
|
| 204 |
+
for news in sample_news:
|
| 205 |
+
result = sentiment_analyzer(news)[0]
|
| 206 |
+
sentiments.append({
|
| 207 |
+
'text': news,
|
| 208 |
+
'sentiment': result['label'],
|
| 209 |
+
'score': result['score']
|
| 210 |
+
})
|
| 211 |
+
|
| 212 |
+
return sentiments
|
| 213 |
+
|
| 214 |
+
except Exception as e:
|
| 215 |
+
return [{'text': f'新聞分析暫時無法使用: {str(e)}', 'sentiment': 'NEUTRAL', 'score': 0.5}]
|
| 216 |
+
|
| 217 |
+
def analyze_sentiment_summary(self, sentiments):
|
| 218 |
+
"""分析情感摘要"""
|
| 219 |
+
if not sentiments:
|
| 220 |
+
return "中性"
|
| 221 |
+
|
| 222 |
+
positive_count = sum(1 for s in sentiments if s['sentiment'] == 'POSITIVE')
|
| 223 |
+
negative_count = sum(1 for s in sentiments if s['sentiment'] == 'NEGATIVE')
|
| 224 |
+
|
| 225 |
+
if positive_count > negative_count:
|
| 226 |
+
return "偏樂觀"
|
| 227 |
+
elif negative_count > positive_count:
|
| 228 |
+
return "偏悲觀"
|
| 229 |
+
else:
|
| 230 |
+
return "中性"
|
| 231 |
+
|
| 232 |
+
def calculate_prediction_probabilities(self, technical_signals, sentiment, recent_data):
|
| 233 |
+
"""計算上漲和下跌機率"""
|
| 234 |
+
# 計算技術面得分
|
| 235 |
+
bullish_signals = sum(1 for signal in technical_signals if "多頭" in signal or "機會" in signal)
|
| 236 |
+
bearish_signals = sum(1 for signal in technical_signals if "空頭" in signal or "警訊" in signal)
|
| 237 |
+
neutral_signals = len(technical_signals) - bullish_signals - bearish_signals
|
| 238 |
+
|
| 239 |
+
# 技術面得分 (-1 到 1)
|
| 240 |
+
total_signals = len(technical_signals)
|
| 241 |
+
if total_signals > 0:
|
| 242 |
+
tech_score = (bullish_signals - bearish_signals) / total_signals
|
| 243 |
+
else:
|
| 244 |
+
tech_score = 0
|
| 245 |
+
|
| 246 |
+
# 情感得分 (-1 到 1)
|
| 247 |
+
sentiment_score = 0
|
| 248 |
+
if sentiment == "偏樂觀":
|
| 249 |
+
sentiment_score = 0.6
|
| 250 |
+
elif sentiment == "偏悲觀":
|
| 251 |
+
sentiment_score = -0.6
|
| 252 |
+
else:
|
| 253 |
+
sentiment_score = 0
|
| 254 |
+
|
| 255 |
+
# 價格動量得分
|
| 256 |
+
price_change = ((recent_data['Close'].iloc[-1] - recent_data['Close'].iloc[-5]) / recent_data['Close'].iloc[-5]) * 100
|
| 257 |
+
momentum_score = np.tanh(price_change / 10) # 標準化到 -1 到 1
|
| 258 |
+
|
| 259 |
+
# RSI 得分
|
| 260 |
+
latest = recent_data.iloc[-1]
|
| 261 |
+
rsi = latest.get('RSI', 50)
|
| 262 |
+
if rsi > 70:
|
| 263 |
+
rsi_score = -0.5 # 超買,偏空
|
| 264 |
+
elif rsi < 30:
|
| 265 |
+
rsi_score = 0.5 # 超賣,偏多
|
| 266 |
+
else:
|
| 267 |
+
rsi_score = (50 - rsi) / 100 # 標準化
|
| 268 |
+
|
| 269 |
+
# MACD 得分
|
| 270 |
+
macd_score = 0
|
| 271 |
+
if 'MACD' in latest and 'MACD_signal' in latest:
|
| 272 |
+
if latest['MACD'] > latest['MACD_signal']:
|
| 273 |
+
macd_score = 0.3
|
| 274 |
+
else:
|
| 275 |
+
macd_score = -0.3
|
| 276 |
+
|
| 277 |
+
# 綜合得分計算(加權平均)
|
| 278 |
+
weights = {
|
| 279 |
+
'tech': 0.25,
|
| 280 |
+
'sentiment': 0.20,
|
| 281 |
+
'momentum': 0.25,
|
| 282 |
+
'rsi': 0.15,
|
| 283 |
+
'macd': 0.15
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
total_score = (
|
| 287 |
+
tech_score * weights['tech'] +
|
| 288 |
+
sentiment_score * weights['sentiment'] +
|
| 289 |
+
momentum_score * weights['momentum'] +
|
| 290 |
+
rsi_score * weights['rsi'] +
|
| 291 |
+
macd_score * weights['macd']
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
# 將得分轉換為機率 (使用 sigmoid 函數)
|
| 295 |
+
def sigmoid(x):
|
| 296 |
+
return 1 / (1 + np.exp(-x * 3)) # 放大 3 倍讓機率更明顯
|
| 297 |
+
|
| 298 |
+
up_probability = sigmoid(total_score) * 100
|
| 299 |
+
down_probability = sigmoid(-total_score) * 100
|
| 300 |
+
sideways_probability = 100 - up_probability - down_probability
|
| 301 |
+
|
| 302 |
+
# 確保機率總和為 100%
|
| 303 |
+
total_prob = up_probability + down_probability + sideways_probability
|
| 304 |
+
up_probability = (up_probability / total_prob) * 100
|
| 305 |
+
down_probability = (down_probability / total_prob) * 100
|
| 306 |
+
sideways_probability = (sideways_probability / total_prob) * 100
|
| 307 |
+
|
| 308 |
+
return {
|
| 309 |
+
'up': max(15, min(75, up_probability)), # 限制在 15%-75% 範圍內
|
| 310 |
+
'down': max(15, min(75, down_probability)), # 限制在 15%-75% 範圍內
|
| 311 |
+
'sideways': max(10, sideways_probability), # 至少 10%
|
| 312 |
+
'confidence': abs(total_score) # 信心度
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
def generate_comprehensive_prediction(self, technical_signals, sentiment, recent_data):
|
| 316 |
+
"""生成綜合預測報告"""
|
| 317 |
+
# 計算價格變化
|
| 318 |
+
price_change = ((recent_data['Close'].iloc[-1] - recent_data['Close'].iloc[-5]) / recent_data['Close'].iloc[-5]) * 100
|
| 319 |
+
|
| 320 |
+
# 計算預測機率
|
| 321 |
+
probabilities = self.calculate_prediction_probabilities(technical_signals, sentiment, recent_data)
|
| 322 |
+
|
| 323 |
+
# 確定主要預測方向
|
| 324 |
+
max_prob = max(probabilities['up'], probabilities['down'], probabilities['sideways'])
|
| 325 |
+
if probabilities['up'] == max_prob:
|
| 326 |
+
main_direction = "看多"
|
| 327 |
+
direction_emoji = "📈"
|
| 328 |
+
elif probabilities['down'] == max_prob:
|
| 329 |
+
main_direction = "看空"
|
| 330 |
+
direction_emoji = "📉"
|
| 331 |
+
else:
|
| 332 |
+
main_direction = "盤整"
|
| 333 |
+
direction_emoji = "➡️"
|
| 334 |
+
|
| 335 |
+
# 信心度描述
|
| 336 |
+
confidence = probabilities['confidence']
|
| 337 |
+
if confidence > 0.4:
|
| 338 |
+
confidence_desc = "高信心"
|
| 339 |
+
elif confidence > 0.2:
|
| 340 |
+
confidence_desc = "中等信心"
|
| 341 |
+
else:
|
| 342 |
+
confidence_desc = "低信心"
|
| 343 |
+
|
| 344 |
+
report = f"""
|
| 345 |
+
## 📊 {self.symbol} AI 分析報告
|
| 346 |
+
|
| 347 |
+
### 📈 技術面分析:
|
| 348 |
+
{chr(10).join(f"• {signal}" for signal in technical_signals)}
|
| 349 |
+
|
| 350 |
+
### 💭 市場情感:{sentiment}
|
| 351 |
+
|
| 352 |
+
### 📊 近期表現:
|
| 353 |
+
- 5日漲跌幅:{price_change:+.2f}%
|
| 354 |
+
- 當前價位:${recent_data['Close'].iloc[-1]:.2f}
|
| 355 |
+
|
| 356 |
+
### 🤖 AI 預測機率(短期 1-7天):
|
| 357 |
+
|
| 358 |
+
| 方向 | 機率 | 說明 |
|
| 359 |
+
|------|------|------|
|
| 360 |
+
| 📈 **上漲** | **{probabilities['up']:.1f}%** | 股價向上突破的可能性 |
|
| 361 |
+
| 📉 **下跌** | **{probabilities['down']:.1f}%** | 股價向下修正的可能性 |
|
| 362 |
+
| ➡️ **盤整** | **{probabilities['sideways']:.1f}%** | 股價維持震盪的可能性 |
|
| 363 |
+
|
| 364 |
+
### 🎯 主要預測方向:
|
| 365 |
+
{direction_emoji} **{main_direction}** ({confidence_desc} - {confidence*100:.0f}%)
|
| 366 |
+
|
| 367 |
+
### 📋 投資建議:
|
| 368 |
+
"""
|
| 369 |
+
|
| 370 |
+
# 根據最高機率給出建議
|
| 371 |
+
if probabilities['up'] > 50:
|
| 372 |
+
report += """
|
| 373 |
+
- 💡 **多頭策略**:考慮逢低加碼或持有現有部位
|
| 374 |
+
- 🎯 **目標設定**:關注上方阻力位,設定合理獲利目標
|
| 375 |
+
- 🛡️ **風險管理**:設置止損點保護資本"""
|
| 376 |
+
elif probabilities['down'] > 50:
|
| 377 |
+
report += """
|
| 378 |
+
- 💡 **防守策略**:考慮減碼或等待更佳進場點
|
| 379 |
+
- 🎯 **支撐觀察**:留意下方支撐位是否守住
|
| 380 |
+
- 🛡️ **風險管理**:避免追高,控制倉位大小"""
|
| 381 |
+
else:
|
| 382 |
+
report += """
|
| 383 |
+
- 💡 **中性策略**:保持觀望,等待明確方向訊號
|
| 384 |
+
- 🎯 **區間操作**:可考慮在支撐阻力區間內操作
|
| 385 |
+
- 🛡️ **風險管理**:小部位測試,嚴格執行停損"""
|
| 386 |
+
|
| 387 |
+
report += f"""
|
| 388 |
+
|
| 389 |
+
### 📅 中期展望(1個月):
|
| 390 |
+
基於當前技術面和市場情緒分析,建議持續關注:
|
| 391 |
+
- 關鍵技術位:支撐與阻力區間
|
| 392 |
+
- 市場情緒變化:新聞面和資金流向
|
| 393 |
+
- 整體大盤走勢:系統性風險評估
|
| 394 |
+
|
| 395 |
+
⚠️ **風險提醒**:此分析基於歷史數據和 AI 模型預測,僅供參考。投資有風險,請謹慎評估並做好風險管理!
|
| 396 |
+
|
| 397 |
+
---
|
| 398 |
+
*預測信心度:{confidence*100:.0f}% | 分析時間:{datetime.now().strftime('%Y-%m-%d %H:%M')}*
|
| 399 |
+
"""
|
| 400 |
+
|
| 401 |
+
return report
|
| 402 |
+
|
| 403 |
+
def generate_prediction(self, df, news_sentiment):
|
| 404 |
+
"""生成預測分析"""
|
| 405 |
+
if df is None or len(df) < 30:
|
| 406 |
+
return "數據不足,無法進行預測分析"
|
| 407 |
+
|
| 408 |
+
# 獲取最新數據
|
| 409 |
+
latest = df.iloc[-1]
|
| 410 |
+
recent_data = df.tail(20)
|
| 411 |
+
|
| 412 |
+
# 技術分析信號
|
| 413 |
+
technical_signals = []
|
| 414 |
+
|
| 415 |
+
# 價格趋势
|
| 416 |
+
if latest['Close'] > latest['MA20']:
|
| 417 |
+
technical_signals.append("價格在20日均線之上(多頭信號)")
|
| 418 |
+
else:
|
| 419 |
+
technical_signals.append("價格在20日均線之下(空頭信號)")
|
| 420 |
+
|
| 421 |
+
# RSI 分析
|
| 422 |
+
rsi = latest['RSI']
|
| 423 |
+
if rsi > 70:
|
| 424 |
+
technical_signals.append(f"RSI({rsi:.1f}) 超買警訊")
|
| 425 |
+
elif rsi < 30:
|
| 426 |
+
technical_signals.append(f"RSI({rsi:.1f}) 超賣機會")
|
| 427 |
+
else:
|
| 428 |
+
technical_signals.append(f"RSI({rsi:.1f}) 正常範圍")
|
| 429 |
+
|
| 430 |
+
# MACD 分析
|
| 431 |
+
if latest['MACD'] > latest['MACD_signal']:
|
| 432 |
+
technical_signals.append("MACD 呈現多頭排列")
|
| 433 |
+
else:
|
| 434 |
+
technical_signals.append("MACD 呈現空頭排列")
|
| 435 |
+
|
| 436 |
+
# 新聞情感分析
|
| 437 |
+
sentiment_summary = self.analyze_sentiment_summary(news_sentiment)
|
| 438 |
+
|
| 439 |
+
# 綜合預測
|
| 440 |
+
prediction = self.generate_comprehensive_prediction(technical_signals, sentiment_summary, recent_data)
|
| 441 |
+
|
| 442 |
+
return prediction
|
| 443 |
+
|
| 444 |
+
# 創建分析器實例
|
| 445 |
+
analyzer = StockAnalyzer()
|
| 446 |
+
|
| 447 |
+
def analyze_stock(symbol):
|
| 448 |
+
"""主要分析函數"""
|
| 449 |
+
if not symbol.strip():
|
| 450 |
+
return None, "請輸入股票代碼", ""
|
| 451 |
+
|
| 452 |
+
# 獲取數據
|
| 453 |
+
result = analyzer.fetch_stock_data(symbol.upper())
|
| 454 |
+
if len(result) == 3:
|
| 455 |
+
success, message, stock_name = result
|
| 456 |
+
else:
|
| 457 |
+
success, message = result
|
| 458 |
+
stock_name = None
|
| 459 |
+
|
| 460 |
+
if not success:
|
| 461 |
+
return None, message, ""
|
| 462 |
+
|
| 463 |
+
# 計算技術指標
|
| 464 |
+
df = analyzer.calculate_technical_indicators()
|
| 465 |
+
|
| 466 |
+
# 創建價格圖表
|
| 467 |
+
fig = go.Figure()
|
| 468 |
+
|
| 469 |
+
# 添加K線圖
|
| 470 |
+
fig.add_trace(go.Candlestick(
|
| 471 |
+
x=df.index,
|
| 472 |
+
open=df['Open'],
|
| 473 |
+
high=df['High'],
|
| 474 |
+
low=df['Low'],
|
| 475 |
+
close=df['Close'],
|
| 476 |
+
name='價格'
|
| 477 |
+
))
|
| 478 |
+
|
| 479 |
+
# 添加移動平均線
|
| 480 |
+
fig.add_trace(go.Scatter(x=df.index, y=df['MA5'], name='MA5', line=dict(color='orange')))
|
| 481 |
+
fig.add_trace(go.Scatter(x=df.index, y=df['MA20'], name='MA20', line=dict(color='blue')))
|
| 482 |
+
|
| 483 |
+
fig.update_layout(
|
| 484 |
+
title=f'{symbol} 股價走勢與技術指標',
|
| 485 |
+
xaxis_title='日期',
|
| 486 |
+
yaxis_title='價格',
|
| 487 |
+
height=600
|
| 488 |
+
)
|
| 489 |
+
|
| 490 |
+
# 獲取新聞情感
|
| 491 |
+
news_sentiment = analyzer.get_news_sentiment(symbol)
|
| 492 |
+
|
| 493 |
+
# 生成預測
|
| 494 |
+
prediction = analyzer.generate_prediction(df, news_sentiment)
|
| 495 |
+
|
| 496 |
+
return fig, "分析完成!", prediction
|
| 497 |
+
|
| 498 |
+
def create_results_table(results):
|
| 499 |
+
"""創建結果表格"""
|
| 500 |
+
if not results:
|
| 501 |
+
return ""
|
| 502 |
+
|
| 503 |
+
# 創建表格 HTML
|
| 504 |
+
table_html = """
|
| 505 |
+
<div style="overflow-x: auto; margin: 20px 0;">
|
| 506 |
+
<table style="width: 100%; border-collapse: collapse; font-family: Arial, sans-serif;">
|
| 507 |
+
<thead>
|
| 508 |
+
<tr style="background-color: #f0f0f0;">
|
| 509 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: left;">股票代號</th>
|
| 510 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: left;">股票名稱</th>
|
| 511 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: right;">當前價格</th>
|
| 512 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: right;">上漲機率(%)</th>
|
| 513 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: right;">下跌機率(%)</th>
|
| 514 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: right;">盤整機率(%)</th>
|
| 515 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: right;">信心度(%)</th>
|
| 516 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: center;">預測方向</th>
|
| 517 |
+
<th style="border: 1px solid #ddd; padding: 12px; text-align: left;">狀態</th>
|
| 518 |
+
</tr>
|
| 519 |
+
</thead>
|
| 520 |
+
<tbody>
|
| 521 |
+
"""
|
| 522 |
+
|
| 523 |
+
for result in results:
|
| 524 |
+
# 判斷預測方向和顏色
|
| 525 |
+
if result['error_message']:
|
| 526 |
+
direction = "❌ 錯誤"
|
| 527 |
+
row_color = "#fff2f2"
|
| 528 |
+
else:
|
| 529 |
+
up_prob = float(result['up_probability'])
|
| 530 |
+
down_prob = float(result['down_probability'])
|
| 531 |
+
sideways_prob = float(result['sideways_probability'])
|
| 532 |
+
|
| 533 |
+
if up_prob > down_prob and up_prob > sideways_prob:
|
| 534 |
+
direction = "📈 看多"
|
| 535 |
+
row_color = "#f0fff0" # 淡綠色
|
| 536 |
+
elif down_prob > up_prob and down_prob > sideways_prob:
|
| 537 |
+
direction = "📉 看空"
|
| 538 |
+
row_color = "#fff0f0" # 淡紅色
|
| 539 |
+
else:
|
| 540 |
+
direction = "➡️ 盤整"
|
| 541 |
+
row_color = "#f8f8f8" # 淡灰色
|
| 542 |
+
|
| 543 |
+
status = "✅ 成功" if not result['error_message'] else f"❌ {result['error_message'][:30]}..."
|
| 544 |
+
|
| 545 |
+
table_html += f"""
|
| 546 |
+
<tr style="background-color: {row_color};">
|
| 547 |
+
<td style="border: 1px solid #ddd; padding: 8px; font-weight: bold;">{result['symbol']}</td>
|
| 548 |
+
<td style="border: 1px solid #ddd; padding: 8px;">{result['name']}</td>
|
| 549 |
+
<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">{result['current_price']}</td>
|
| 550 |
+
<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">{result['up_probability']}</td>
|
| 551 |
+
<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">{result['down_probability']}</td>
|
| 552 |
+
<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">{result['sideways_probability']}</td>
|
| 553 |
+
<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">{result['confidence']}</td>
|
| 554 |
+
<td style="border: 1px solid #ddd; padding: 8px; text-align: center;">{direction}</td>
|
| 555 |
+
<td style="border: 1px solid #ddd; padding: 8px;">{status}</td>
|
| 556 |
+
</tr>
|
| 557 |
+
"""
|
| 558 |
+
|
| 559 |
+
table_html += """
|
| 560 |
+
</tbody>
|
| 561 |
+
</table>
|
| 562 |
+
</div>
|
| 563 |
+
"""
|
| 564 |
+
|
| 565 |
+
return table_html
|
| 566 |
+
|
| 567 |
+
def create_batch_analysis_charts(results):
|
| 568 |
+
"""創建批次分析結果圖表"""
|
| 569 |
+
if not results:
|
| 570 |
+
return None, None, None, None
|
| 571 |
+
|
| 572 |
+
# 過濾出成功分析的結果
|
| 573 |
+
success_results = [r for r in results if r['error_message'] == '']
|
| 574 |
+
|
| 575 |
+
if not success_results:
|
| 576 |
+
return None, None, None, None
|
| 577 |
+
|
| 578 |
+
# 準備數據
|
| 579 |
+
symbols = [r['symbol'] for r in success_results]
|
| 580 |
+
up_probs = [float(r['up_probability']) for r in success_results]
|
| 581 |
+
down_probs = [float(r['down_probability']) for r in success_results]
|
| 582 |
+
sideways_probs = [float(r['sideways_probability']) for r in success_results]
|
| 583 |
+
confidence = [float(r['confidence']) for r in success_results]
|
| 584 |
+
|
| 585 |
+
# 1. 機率比較柱狀圖
|
| 586 |
+
fig_bar = go.Figure()
|
| 587 |
+
fig_bar.add_trace(go.Bar(name='上漲機率', x=symbols, y=up_probs, marker_color='green', opacity=0.8))
|
| 588 |
+
fig_bar.add_trace(go.Bar(name='下跌機率', x=symbols, y=down_probs, marker_color='red', opacity=0.8))
|
| 589 |
+
fig_bar.add_trace(go.Bar(name='盤整機率', x=symbols, y=sideways_probs, marker_color='gray', opacity=0.8))
|
| 590 |
+
|
| 591 |
+
fig_bar.update_layout(
|
| 592 |
+
title='📊 股票預測機率比較',
|
| 593 |
+
xaxis_title='股票代號',
|
| 594 |
+
yaxis_title='機率 (%)',
|
| 595 |
+
barmode='group',
|
| 596 |
+
height=500,
|
| 597 |
+
showlegend=True,
|
| 598 |
+
xaxis_tickangle=-45
|
| 599 |
+
)
|
| 600 |
+
|
| 601 |
+
# 2. 信心度散佈圖
|
| 602 |
+
fig_scatter = go.Figure()
|
| 603 |
+
|
| 604 |
+
# 根據最高機率決定顏色
|
| 605 |
+
colors = []
|
| 606 |
+
for i in range(len(success_results)):
|
| 607 |
+
if up_probs[i] > down_probs[i] and up_probs[i] > sideways_probs[i]:
|
| 608 |
+
colors.append('green') # 看多
|
| 609 |
+
elif down_probs[i] > up_probs[i] and down_probs[i] > sideways_probs[i]:
|
| 610 |
+
colors.append('red') # 看空
|
| 611 |
+
else:
|
| 612 |
+
colors.append('gray') # 盤整
|
| 613 |
+
|
| 614 |
+
fig_scatter.add_trace(go.Scatter(
|
| 615 |
+
x=symbols,
|
| 616 |
+
y=confidence,
|
| 617 |
+
mode='markers+text',
|
| 618 |
+
marker=dict(
|
| 619 |
+
size=[max(prob) for prob in zip(up_probs, down_probs, sideways_probs)],
|
| 620 |
+
sizemode='diameter',
|
| 621 |
+
sizeref=2,
|
| 622 |
+
color=colors,
|
| 623 |
+
opacity=0.7,
|
| 624 |
+
line=dict(width=2, color='white')
|
| 625 |
+
),
|
| 626 |
+
text=[f"{conf:.1f}%" for conf in confidence],
|
| 627 |
+
textposition="middle center",
|
| 628 |
+
name='信心度'
|
| 629 |
+
))
|
| 630 |
+
|
| 631 |
+
fig_scatter.update_layout(
|
| 632 |
+
title='🎯 預測信心度分佈 (圓圈大小=最高機率)',
|
| 633 |
+
xaxis_title='股票代號',
|
| 634 |
+
yaxis_title='信心度 (%)',
|
| 635 |
+
height=500,
|
| 636 |
+
xaxis_tickangle=-45
|
| 637 |
+
)
|
| 638 |
+
|
| 639 |
+
# 3. 綜合評分雷達圖 (取前6支股票)
|
| 640 |
+
radar_data = success_results[:6] # 限制顯示數量避免過於擁擠
|
| 641 |
+
fig_radar = go.Figure()
|
| 642 |
+
|
| 643 |
+
categories = ['上漲機率', '信心度', '綜合評分']
|
| 644 |
+
|
| 645 |
+
for i, result in enumerate(radar_data):
|
| 646 |
+
# 計算綜合評分 (上漲機率 * 信心度 / 100)
|
| 647 |
+
composite_score = float(result['up_probability']) * float(result['confidence']) / 100
|
| 648 |
+
|
| 649 |
+
values = [
|
| 650 |
+
float(result['up_probability']),
|
| 651 |
+
float(result['confidence']),
|
| 652 |
+
composite_score
|
| 653 |
+
]
|
| 654 |
+
|
| 655 |
+
fig_radar.add_trace(go.Scatterpolar(
|
| 656 |
+
r=values + [values[0]], # 閉合雷達圖
|
| 657 |
+
theta=categories + [categories[0]],
|
| 658 |
+
fill='toself',
|
| 659 |
+
name=result['symbol'],
|
| 660 |
+
opacity=0.6
|
| 661 |
+
))
|
| 662 |
+
|
| 663 |
+
fig_radar.update_layout(
|
| 664 |
+
polar=dict(
|
| 665 |
+
radialaxis=dict(
|
| 666 |
+
visible=True,
|
| 667 |
+
range=[0, 100]
|
| 668 |
+
)
|
| 669 |
+
),
|
| 670 |
+
title='📈 股票綜合評分雷達圖 (前6支)',
|
| 671 |
+
height=500,
|
| 672 |
+
showlegend=True
|
| 673 |
+
)
|
| 674 |
+
|
| 675 |
+
# 4. 機率分佈餅圖統計
|
| 676 |
+
# 統計各種預測傾向的數量
|
| 677 |
+
bullish_count = sum(1 for r in success_results if float(r['up_probability']) > max(float(r['down_probability']), float(r['sideways_probability'])))
|
| 678 |
+
bearish_count = sum(1 for r in success_results if float(r['down_probability']) > max(float(r['up_probability']), float(r['sideways_probability'])))
|
| 679 |
+
neutral_count = len(success_results) - bullish_count - bearish_count
|
| 680 |
+
|
| 681 |
+
fig_pie = go.Figure(data=[go.Pie(
|
| 682 |
+
labels=['看多股票', '看空股票', '盤整股票'],
|
| 683 |
+
values=[bullish_count, bearish_count, neutral_count],
|
| 684 |
+
marker_colors=['green', 'red', 'gray'],
|
| 685 |
+
textinfo='label+percent+value',
|
| 686 |
+
hovertemplate='<b>%{label}</b><br>數量: %{value}<br>比例: %{percent}<extra></extra>'
|
| 687 |
+
)])
|
| 688 |
+
|
| 689 |
+
fig_pie.update_layout(
|
| 690 |
+
title='🥧 整體市場情緒分佈',
|
| 691 |
+
height=400
|
| 692 |
+
)
|
| 693 |
+
|
| 694 |
+
return fig_bar, fig_scatter, fig_radar, fig_pie
|
| 695 |
+
|
| 696 |
+
def batch_analyze_stocks(stock_input_text):
|
| 697 |
+
"""批次分析股票清單"""
|
| 698 |
+
# 檢查輸入是否為空
|
| 699 |
+
if not stock_input_text or not stock_input_text.strip():
|
| 700 |
+
return "❌ 請輸入股票代號!", "", None, None, None, None, ""
|
| 701 |
+
|
| 702 |
+
try:
|
| 703 |
+
# 從文字輸入框解析股票清單
|
| 704 |
+
# 支援多種分隔符:換行、逗號、分號、空格
|
| 705 |
+
import re
|
| 706 |
+
stock_symbols = re.split(r'[,;\s\n]+', stock_input_text.strip())
|
| 707 |
+
stock_symbols = [symbol.strip().upper() for symbol in stock_symbols if symbol.strip()]
|
| 708 |
+
|
| 709 |
+
if not stock_symbols:
|
| 710 |
+
return "❌ 未能解析出有效的股票代號!", "", None, None, None, None, ""
|
| 711 |
+
|
| 712 |
+
# 準備結果列表
|
| 713 |
+
results = []
|
| 714 |
+
progress_messages = []
|
| 715 |
+
|
| 716 |
+
progress_messages.append(f"📊 開始批次分析 {len(stock_symbols)} 支股票...")
|
| 717 |
+
|
| 718 |
+
# 分析每支股票
|
| 719 |
+
for i, symbol in enumerate(stock_symbols, 1):
|
| 720 |
+
progress_messages.append(f"\n🔍 正在分析 ({i}/{len(stock_symbols)}): {symbol}")
|
| 721 |
+
|
| 722 |
+
try:
|
| 723 |
+
# 獲取股票數據
|
| 724 |
+
result = analyzer.fetch_stock_data(symbol.upper())
|
| 725 |
+
if len(result) == 3:
|
| 726 |
+
success, message, stock_name = result
|
| 727 |
+
else:
|
| 728 |
+
success, message = result
|
| 729 |
+
stock_name = symbol
|
| 730 |
+
|
| 731 |
+
if not success:
|
| 732 |
+
# 記錄錯誤
|
| 733 |
+
results.append({
|
| 734 |
+
'symbol': symbol,
|
| 735 |
+
'name': stock_name or symbol,
|
| 736 |
+
'current_price': 'N/A',
|
| 737 |
+
'up_probability': 'ERROR',
|
| 738 |
+
'down_probability': 'ERROR',
|
| 739 |
+
'sideways_probability': 'ERROR',
|
| 740 |
+
'confidence': 'ERROR',
|
| 741 |
+
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
| 742 |
+
'error_message': message
|
| 743 |
+
})
|
| 744 |
+
progress_messages.append(f"❌ {symbol}: {message}")
|
| 745 |
+
continue
|
| 746 |
+
|
| 747 |
+
# 計算技術指標
|
| 748 |
+
df = analyzer.calculate_technical_indicators()
|
| 749 |
+
if df is None or len(df) < 30:
|
| 750 |
+
results.append({
|
| 751 |
+
'symbol': symbol,
|
| 752 |
+
'name': stock_name or symbol,
|
| 753 |
+
'current_price': 'N/A',
|
| 754 |
+
'up_probability': 'ERROR',
|
| 755 |
+
'down_probability': 'ERROR',
|
| 756 |
+
'sideways_probability': 'ERROR',
|
| 757 |
+
'confidence': 'ERROR',
|
| 758 |
+
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
| 759 |
+
'error_message': '數據不足,無法分析'
|
| 760 |
+
})
|
| 761 |
+
progress_messages.append(f"❌ {symbol}: 數據不足")
|
| 762 |
+
continue
|
| 763 |
+
|
| 764 |
+
# 獲取新聞情感
|
| 765 |
+
news_sentiment = analyzer.get_news_sentiment(symbol)
|
| 766 |
+
sentiment_summary = analyzer.analyze_sentiment_summary(news_sentiment)
|
| 767 |
+
|
| 768 |
+
# 計算預測機率
|
| 769 |
+
recent_data = df.tail(20)
|
| 770 |
+
technical_signals = []
|
| 771 |
+
|
| 772 |
+
# 簡化的技術信號計算
|
| 773 |
+
latest = df.iloc[-1]
|
| 774 |
+
if latest['Close'] > latest['MA20']:
|
| 775 |
+
technical_signals.append("價格在20日均線之上")
|
| 776 |
+
else:
|
| 777 |
+
technical_signals.append("價格在20日均線之下")
|
| 778 |
+
|
| 779 |
+
probabilities = analyzer.calculate_prediction_probabilities(
|
| 780 |
+
technical_signals, sentiment_summary, recent_data
|
| 781 |
+
)
|
| 782 |
+
|
| 783 |
+
# 獲取股票資訊
|
| 784 |
+
stock_info = analyzer.get_stock_info(symbol)
|
| 785 |
+
|
| 786 |
+
# 記錄成功結果
|
| 787 |
+
results.append({
|
| 788 |
+
'symbol': symbol,
|
| 789 |
+
'name': stock_info['name'],
|
| 790 |
+
'current_price': f"{latest['Close']:.2f}" if latest['Close'] else 'N/A',
|
| 791 |
+
'up_probability': f"{probabilities['up']:.1f}",
|
| 792 |
+
'down_probability': f"{probabilities['down']:.1f}",
|
| 793 |
+
'sideways_probability': f"{probabilities['sideways']:.1f}",
|
| 794 |
+
'confidence': f"{probabilities['confidence']*100:.1f}",
|
| 795 |
+
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
| 796 |
+
'error_message': ''
|
| 797 |
+
})
|
| 798 |
+
|
| 799 |
+
progress_messages.append(f"✅ {symbol}: 分析完成")
|
| 800 |
+
|
| 801 |
+
except Exception as e:
|
| 802 |
+
# 處理未預期的錯誤
|
| 803 |
+
results.append({
|
| 804 |
+
'symbol': symbol,
|
| 805 |
+
'name': symbol,
|
| 806 |
+
'current_price': 'N/A',
|
| 807 |
+
'up_probability': 'ERROR',
|
| 808 |
+
'down_probability': 'ERROR',
|
| 809 |
+
'sideways_probability': 'ERROR',
|
| 810 |
+
'confidence': 'ERROR',
|
| 811 |
+
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
| 812 |
+
'error_message': f'未預期錯誤: {str(e)}'
|
| 813 |
+
})
|
| 814 |
+
progress_messages.append(f"❌ {symbol}: 未預期錯誤")
|
| 815 |
+
|
| 816 |
+
# 統計結果
|
| 817 |
+
success_count = len([r for r in results if r['error_message'] == ''])
|
| 818 |
+
error_count = len(results) - success_count
|
| 819 |
+
|
| 820 |
+
summary_message = f"""
|
| 821 |
+
📈 批次分析完成!
|
| 822 |
+
|
| 823 |
+
📊 **分析統計:**
|
| 824 |
+
- 總計股票數:{len(stock_symbols)}
|
| 825 |
+
- 成功分析:{success_count}
|
| 826 |
+
- 分析失敗:{error_count}
|
| 827 |
+
|
| 828 |
+
� **圖表已生成:**
|
| 829 |
+
- 📊 機率比較柱狀圖
|
| 830 |
+
- 🎯 信心度散佈圖
|
| 831 |
+
- 📈 綜合評分雷達圖
|
| 832 |
+
- 🥧 市場情緒餅圖
|
| 833 |
+
|
| 834 |
+
🎯 **請查看下方圖表進行投資決策分析!**
|
| 835 |
+
"""
|
| 836 |
+
|
| 837 |
+
progress_log = "\n".join(progress_messages)
|
| 838 |
+
|
| 839 |
+
# 創建圖表和結果表格
|
| 840 |
+
chart_bar, chart_scatter, chart_radar, chart_pie = create_batch_analysis_charts(results)
|
| 841 |
+
results_table = create_results_table(results)
|
| 842 |
+
|
| 843 |
+
return summary_message, progress_log, chart_bar, chart_scatter, chart_radar, chart_pie, results_table
|
| 844 |
+
|
| 845 |
+
except Exception as e:
|
| 846 |
+
return f"❌ 批次分析過程中發生錯誤:{str(e)}", "", None, None, None, None, ""
|
| 847 |
+
|
| 848 |
+
# 創建 Gradio 界面
|
| 849 |
+
with gr.Blocks(title="AI 股票分析師", theme=gr.themes.Soft()) as app:
|
| 850 |
+
# 主題管理腳本注入
|
| 851 |
+
gr.HTML("""
|
| 852 |
+
<script src="file:///static/theme-manager.js"></script>
|
| 853 |
+
<link rel="stylesheet" href="file:///css/dark_mode.css">
|
| 854 |
+
""")
|
| 855 |
+
|
| 856 |
+
# 標題和主題切換控制項
|
| 857 |
+
with gr.Row():
|
| 858 |
+
with gr.Column(scale=1):
|
| 859 |
+
gr.Markdown("# 📈 AI 股票分析師")
|
| 860 |
+
with gr.Column(scale=0, min_width=120):
|
| 861 |
+
theme_toggle_btn = gr.Button(
|
| 862 |
+
"🌙 深色模式",
|
| 863 |
+
elem_classes=["theme-toggle-btn"],
|
| 864 |
+
size="sm"
|
| 865 |
+
)
|
| 866 |
+
|
| 867 |
+
gr.Markdown(
|
| 868 |
+
"""
|
| 869 |
+
### 🤖 使用 Hugging Face 模型進行智能股票分析
|
| 870 |
+
|
| 871 |
+
**✨ 核心功能:**
|
| 872 |
+
- 📊 **完整技術指標**:MA、RSI、MACD、布林通道分析
|
| 873 |
+
- 🧠 **AI 情感分析**:使用 FinBERT 模型分析市場情緒
|
| 874 |
+
- 🎯 **機率預測**:提供上漲/下跌/盤整機率百分比
|
| 875 |
+
- 📈 **智能建議**:根據機率給出個性化投資策略
|
| 876 |
+
- 🖼️ **互動圖表**:動態視覺化技術指標走勢
|
| 877 |
+
- 📁 **批次分析**:一次分析多支股票並匯出CSV報告
|
| 878 |
+
|
| 879 |
+
**🚀 使用方法:** 單支分析輸入股票代碼,批次分析直接在文字框中輸入多個股票代號!
|
| 880 |
+
"""
|
| 881 |
+
)
|
| 882 |
+
|
| 883 |
+
# 建立分頁
|
| 884 |
+
with gr.Tabs():
|
| 885 |
+
with gr.TabItem("🎯 單支股票分析"):
|
| 886 |
+
with gr.Row():
|
| 887 |
+
with gr.Column(scale=1):
|
| 888 |
+
stock_input = gr.Textbox(
|
| 889 |
+
label="股票代碼",
|
| 890 |
+
placeholder="例如:AAPL, TSLA, 2330.TW",
|
| 891 |
+
value="2330.TW"
|
| 892 |
+
)
|
| 893 |
+
analyze_btn = gr.Button("開始分析", variant="primary", size="lg")
|
| 894 |
+
|
| 895 |
+
status_output = gr.Textbox(
|
| 896 |
+
label="分析狀態",
|
| 897 |
+
lines=2,
|
| 898 |
+
interactive=False
|
| 899 |
+
)
|
| 900 |
+
|
| 901 |
+
with gr.Column(scale=2):
|
| 902 |
+
chart_output = gr.Plot(label="股價走勢圖")
|
| 903 |
+
|
| 904 |
+
prediction_output = gr.Markdown(label="AI 分析報告")
|
| 905 |
+
|
| 906 |
+
# 事件綁定
|
| 907 |
+
analyze_btn.click(
|
| 908 |
+
fn=analyze_stock,
|
| 909 |
+
inputs=[stock_input],
|
| 910 |
+
outputs=[chart_output, status_output, prediction_output]
|
| 911 |
+
)
|
| 912 |
+
|
| 913 |
+
# 範例按鈕
|
| 914 |
+
gr.Examples(
|
| 915 |
+
examples=[
|
| 916 |
+
["AAPL"],
|
| 917 |
+
["TSLA"],
|
| 918 |
+
["2330.TW"],
|
| 919 |
+
["MSFT"],
|
| 920 |
+
["GOOGL"]
|
| 921 |
+
],
|
| 922 |
+
inputs=[stock_input]
|
| 923 |
+
)
|
| 924 |
+
|
| 925 |
+
with gr.TabItem("📊 批次股票分析"):
|
| 926 |
+
gr.Markdown(
|
| 927 |
+
"""
|
| 928 |
+
### 📁 批次分析功能
|
| 929 |
+
|
| 930 |
+
**📋 使用方式:**
|
| 931 |
+
1. 在下方輸入框中輸入多個股票代號
|
| 932 |
+
2. 支援多種分隔方式:換行、逗號、分號、空格
|
| 933 |
+
3. 點擊「開始批次分析」按鈕
|
| 934 |
+
4. 查看即時互動圖表分析結果
|
| 935 |
+
|
| 936 |
+
**📈 輸出內容:**
|
| 937 |
+
- 📊 機率比較柱狀圖:直觀對比各股票預測機率
|
| 938 |
+
- 🎯 信心度散佈圖:顯示預測可靠性分佈
|
| 939 |
+
- 📈 綜合評分雷達圖:多維度股票評分比較
|
| 940 |
+
- 🥧 市場情緒餅圖:整體多空情緒統計
|
| 941 |
+
- 📋 詳細結果表格:完整數據一覽
|
| 942 |
+
"""
|
| 943 |
+
)
|
| 944 |
+
|
| 945 |
+
# 股票代號輸入區
|
| 946 |
+
with gr.Row():
|
| 947 |
+
with gr.Column(scale=3):
|
| 948 |
+
stock_input_batch = gr.Textbox(
|
| 949 |
+
label="📝 股票代號清單",
|
| 950 |
+
placeholder="""請輸入多個股票代號,支援多種分隔方式:
|
| 951 |
+
|
| 952 |
+
• 換行分隔:
|
| 953 |
+
2330.TW
|
| 954 |
+
2317.TW
|
| 955 |
+
1303.TW
|
| 956 |
+
|
| 957 |
+
• 逗號分隔:2330.TW, 2317.TW, 1303.TW
|
| 958 |
+
|
| 959 |
+
• 空格分隔:2330.TW 2317.TW 1303.TW
|
| 960 |
+
|
| 961 |
+
• 混合分隔:2330.TW, 2317.TW
|
| 962 |
+
1303.TW; AAPL TSLA""",
|
| 963 |
+
lines=8,
|
| 964 |
+
value="2330.TW\n2317.TW\n1303.TW\n0050.TW"
|
| 965 |
+
)
|
| 966 |
+
with gr.Column(scale=1):
|
| 967 |
+
batch_analyze_btn = gr.Button(
|
| 968 |
+
"🚀 開始批次分析",
|
| 969 |
+
variant="primary",
|
| 970 |
+
size="lg"
|
| 971 |
+
)
|
| 972 |
+
|
| 973 |
+
# 快速範例按鈕
|
| 974 |
+
gr.Markdown("**🚀 快速範例:**")
|
| 975 |
+
|
| 976 |
+
example_tw_btn = gr.Button("🇹🇼 台股熱門", size="sm")
|
| 977 |
+
example_us_btn = gr.Button("🇺🇸 美股科技", size="sm")
|
| 978 |
+
example_etf_btn = gr.Button("📈 熱門ETF", size="sm")
|
| 979 |
+
clear_btn = gr.Button("🗑️ 清空", size="sm")
|
| 980 |
+
|
| 981 |
+
gr.Markdown(
|
| 982 |
+
"""
|
| 983 |
+
**💡 支援格式:**
|
| 984 |
+
- 換行分隔
|
| 985 |
+
- 逗號分隔
|
| 986 |
+
- 空格/分號分隔
|
| 987 |
+
- 混合分隔
|
| 988 |
+
"""
|
| 989 |
+
)
|
| 990 |
+
|
| 991 |
+
with gr.Row():
|
| 992 |
+
with gr.Column(scale=1):
|
| 993 |
+
batch_summary = gr.Markdown(label="📊 分析摘要")
|
| 994 |
+
with gr.Column(scale=1):
|
| 995 |
+
batch_progress = gr.Textbox(
|
| 996 |
+
label="📋 分析進度",
|
| 997 |
+
lines=10,
|
| 998 |
+
interactive=False,
|
| 999 |
+
max_lines=15
|
| 1000 |
+
)
|
| 1001 |
+
|
| 1002 |
+
# 圖表顯示區域
|
| 1003 |
+
gr.Markdown("## 📈 視覺化分析結果")
|
| 1004 |
+
|
| 1005 |
+
with gr.Row():
|
| 1006 |
+
with gr.Column(scale=1):
|
| 1007 |
+
chart_probability = gr.Plot(label="📊 股票預測機率比較")
|
| 1008 |
+
with gr.Column(scale=1):
|
| 1009 |
+
chart_confidence = gr.Plot(label="🎯 預測信心度分佈")
|
| 1010 |
+
|
| 1011 |
+
with gr.Row():
|
| 1012 |
+
with gr.Column(scale=1):
|
| 1013 |
+
chart_radar = gr.Plot(label="📈 綜合評分雷達圖")
|
| 1014 |
+
with gr.Column(scale=1):
|
| 1015 |
+
chart_sentiment = gr.Plot(label="🥧 整體市場情緒分佈")
|
| 1016 |
+
|
| 1017 |
+
# 詳細結果表格
|
| 1018 |
+
gr.Markdown("## 📋 詳細分析結果")
|
| 1019 |
+
results_table = gr.HTML(label="分析結果表格")
|
| 1020 |
+
|
| 1021 |
+
# 快速範例按鈕事件綁定
|
| 1022 |
+
example_tw_btn.click(
|
| 1023 |
+
lambda: "2330.TW\n2317.TW\n2454.TW\n2882.TW\n6505.TW\n2303.TW",
|
| 1024 |
+
outputs=[stock_input_batch]
|
| 1025 |
+
)
|
| 1026 |
+
|
| 1027 |
+
example_us_btn.click(
|
| 1028 |
+
lambda: "AAPL\nMSFT\nGOOGL\nTSLA\nNVDA\nAMZN",
|
| 1029 |
+
outputs=[stock_input_batch]
|
| 1030 |
+
)
|
| 1031 |
+
|
| 1032 |
+
example_etf_btn.click(
|
| 1033 |
+
lambda: "0050.TW\n0056.TW\nVTI\nVOO\nQQQ\nSPY",
|
| 1034 |
+
outputs=[stock_input_batch]
|
| 1035 |
+
)
|
| 1036 |
+
|
| 1037 |
+
clear_btn.click(
|
| 1038 |
+
lambda: "",
|
| 1039 |
+
outputs=[stock_input_batch]
|
| 1040 |
+
)
|
| 1041 |
+
|
| 1042 |
+
# 批次分析事件綁定
|
| 1043 |
+
batch_analyze_btn.click(
|
| 1044 |
+
fn=batch_analyze_stocks,
|
| 1045 |
+
inputs=[stock_input_batch],
|
| 1046 |
+
outputs=[
|
| 1047 |
+
batch_summary,
|
| 1048 |
+
batch_progress,
|
| 1049 |
+
chart_probability,
|
| 1050 |
+
chart_confidence,
|
| 1051 |
+
chart_radar,
|
| 1052 |
+
chart_sentiment,
|
| 1053 |
+
results_table
|
| 1054 |
+
]
|
| 1055 |
+
)
|
| 1056 |
+
|
| 1057 |
+
# 啟動應用
|
| 1058 |
+
if __name__ == "__main__":
|
| 1059 |
+
print("正在啟動 AI 股票分析師...")
|
| 1060 |
+
|
| 1061 |
+
# 簡化的啟動邏輯
|
| 1062 |
+
try:
|
| 1063 |
+
if IS_HUGGINGFACE_SPACE:
|
| 1064 |
+
# Hugging Face Spaces 環境 - 使用預設配置
|
| 1065 |
+
print("在 Hugging Face Spaces 中啟動...")
|
| 1066 |
+
app.launch()
|
| 1067 |
+
else:
|
| 1068 |
+
# 本地環境 - 嘗試多個端口
|
| 1069 |
+
print("在本地環境中啟動...")
|
| 1070 |
+
ports_to_try = [7860, 7861, 7862, 7863, 7864, 7865]
|
| 1071 |
+
|
| 1072 |
+
launched = False
|
| 1073 |
+
for port in ports_to_try:
|
| 1074 |
+
try:
|
| 1075 |
+
print(f"嘗試端口 {port}...")
|
| 1076 |
+
app.launch(
|
| 1077 |
+
share=True,
|
| 1078 |
+
server_name="0.0.0.0",
|
| 1079 |
+
server_port=port,
|
| 1080 |
+
show_error=True,
|
| 1081 |
+
quiet=False
|
| 1082 |
+
)
|
| 1083 |
+
launched = True
|
| 1084 |
+
break
|
| 1085 |
+
except OSError as e:
|
| 1086 |
+
if "port" in str(e).lower():
|
| 1087 |
+
print(f"端口 {port} 不可用,嘗試下一個...")
|
| 1088 |
+
continue
|
| 1089 |
+
else:
|
| 1090 |
+
raise e
|
| 1091 |
+
|
| 1092 |
+
if not launched:
|
| 1093 |
+
print("所有預設端口都被佔用,使用隨機端口...")
|
| 1094 |
+
app.launch(
|
| 1095 |
+
share=True,
|
| 1096 |
+
server_name="0.0.0.0",
|
| 1097 |
+
server_port=0, # 0 表示自動分配端口
|
| 1098 |
+
show_error=True
|
| 1099 |
+
)
|
| 1100 |
+
|
| 1101 |
+
except Exception as e:
|
| 1102 |
+
print(f"啟動失敗: {e}")
|
| 1103 |
+
print("請檢查端口使用情況或嘗試重新啟動")
|
| 1104 |
+
raise e
|
css/dark_mode.css
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* 深色模式樣式表 - CSS 變數定義和主題支援 */
|
| 2 |
+
|
| 3 |
+
:root {
|
| 4 |
+
/* 亮色主題變數(預設) */
|
| 5 |
+
--primary-color: #1f77b4;
|
| 6 |
+
--secondary-color: #ff7f0e;
|
| 7 |
+
--accent-color: #2ca02c;
|
| 8 |
+
--bg-color: #ffffff;
|
| 9 |
+
--surface-color: #f8f9fa;
|
| 10 |
+
--text-color: #212529;
|
| 11 |
+
--text-secondary-color: #6c757d;
|
| 12 |
+
--border-color: #dee2e6;
|
| 13 |
+
--hover-color: #e9ecef;
|
| 14 |
+
--success-color: #28a745;
|
| 15 |
+
--warning-color: #ffc107;
|
| 16 |
+
--error-color: #dc3545;
|
| 17 |
+
|
| 18 |
+
/* 過渡設定 */
|
| 19 |
+
--transition-duration: 0.3s;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/* 深色主題變數 */
|
| 23 |
+
[data-theme="dark"] {
|
| 24 |
+
--primary-color: #4a9eff;
|
| 25 |
+
--secondary-color: #ffb366;
|
| 26 |
+
--accent-color: #66dd88;
|
| 27 |
+
--bg-color: #1a1a1a;
|
| 28 |
+
--surface-color: #2d2d2d;
|
| 29 |
+
--text-color: #e8e8e8;
|
| 30 |
+
--text-secondary-color: #a0a0a0;
|
| 31 |
+
--border-color: #404040;
|
| 32 |
+
--hover-color: #383838;
|
| 33 |
+
--success-color: #4ade80;
|
| 34 |
+
--warning-color: #facc15;
|
| 35 |
+
--error-color: #ef4444;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
/* 基礎樣式應用 */
|
| 39 |
+
body {
|
| 40 |
+
background-color: var(--bg-color);
|
| 41 |
+
color: var(--text-color);
|
| 42 |
+
transition: background-color var(--transition-duration), color var(--transition-duration);
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
/* 容器和卡片 */
|
| 46 |
+
.container,
|
| 47 |
+
.card,
|
| 48 |
+
.panel {
|
| 49 |
+
background-color: var(--surface-color);
|
| 50 |
+
border-color: var(--border-color);
|
| 51 |
+
color: var(--text-color);
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
/* 按鈕樣式 */
|
| 55 |
+
button,
|
| 56 |
+
.btn {
|
| 57 |
+
background-color: var(--primary-color);
|
| 58 |
+
color: var(--bg-color);
|
| 59 |
+
border: none;
|
| 60 |
+
border-radius: 4px;
|
| 61 |
+
padding: 8px 16px;
|
| 62 |
+
cursor: pointer;
|
| 63 |
+
transition: background-color var(--transition-duration),
|
| 64 |
+
transform var(--transition-duration);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
button:hover,
|
| 68 |
+
.btn:hover {
|
| 69 |
+
background-color: var(--accent-color);
|
| 70 |
+
transform: translateY(-2px);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
button:focus,
|
| 74 |
+
.btn:focus {
|
| 75 |
+
outline: 2px solid var(--accent-color);
|
| 76 |
+
outline-offset: 2px;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
/* 主題切換按鈕特定樣式 */
|
| 80 |
+
.theme-toggle-btn {
|
| 81 |
+
background-color: var(--surface-color);
|
| 82 |
+
color: var(--text-color);
|
| 83 |
+
border: 1px solid var(--border-color);
|
| 84 |
+
padding: 8px 12px;
|
| 85 |
+
border-radius: 4px;
|
| 86 |
+
cursor: pointer;
|
| 87 |
+
font-size: 14px;
|
| 88 |
+
transition: all var(--transition-duration);
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
.theme-toggle-btn:hover {
|
| 92 |
+
background-color: var(--hover-color);
|
| 93 |
+
border-color: var(--accent-color);
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
.theme-toggle-btn:focus {
|
| 97 |
+
outline: 2px solid var(--accent-color);
|
| 98 |
+
outline-offset: 2px;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
/* 輸入欄位樣式 */
|
| 102 |
+
input,
|
| 103 |
+
textarea,
|
| 104 |
+
select {
|
| 105 |
+
background-color: var(--surface-color);
|
| 106 |
+
color: var(--text-color);
|
| 107 |
+
border: 1px solid var(--border-color);
|
| 108 |
+
padding: 8px 12px;
|
| 109 |
+
border-radius: 4px;
|
| 110 |
+
transition: border-color var(--transition-duration);
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
input:focus,
|
| 114 |
+
textarea:focus,
|
| 115 |
+
select:focus {
|
| 116 |
+
outline: none;
|
| 117 |
+
border-color: var(--accent-color);
|
| 118 |
+
box-shadow: 0 0 4px var(--accent-color);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/* 標籤和文字 */
|
| 122 |
+
label {
|
| 123 |
+
color: var(--text-color);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
a {
|
| 127 |
+
color: var(--primary-color);
|
| 128 |
+
text-decoration: none;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
a:hover {
|
| 132 |
+
color: var(--accent-color);
|
| 133 |
+
text-decoration: underline;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
/* 表格樣式 */
|
| 137 |
+
table {
|
| 138 |
+
background-color: var(--surface-color);
|
| 139 |
+
border-collapse: collapse;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
th {
|
| 143 |
+
background-color: var(--hover-color);
|
| 144 |
+
color: var(--text-color);
|
| 145 |
+
border-bottom: 2px solid var(--border-color);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
td {
|
| 149 |
+
border-bottom: 1px solid var(--border-color);
|
| 150 |
+
padding: 12px;
|
| 151 |
+
color: var(--text-color);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
tr:hover {
|
| 155 |
+
background-color: var(--hover-color);
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
/* 狀態顏色 */
|
| 159 |
+
.success {
|
| 160 |
+
color: var(--success-color);
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
.warning {
|
| 164 |
+
color: var(--warning-color);
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
.error {
|
| 168 |
+
color: var(--error-color);
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
/* Gradio 特定樣式 */
|
| 172 |
+
.gradio-container {
|
| 173 |
+
background-color: var(--bg-color) !important;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
.gradio-textbox,
|
| 177 |
+
.gradio-number,
|
| 178 |
+
.gradio-slider {
|
| 179 |
+
background-color: var(--surface-color) !important;
|
| 180 |
+
color: var(--text-color) !important;
|
| 181 |
+
border-color: var(--border-color) !important;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
.gradio-button {
|
| 185 |
+
background-color: var(--primary-color) !important;
|
| 186 |
+
color: var(--bg-color) !important;
|
| 187 |
+
border: none !important;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.gradio-button:hover {
|
| 191 |
+
background-color: var(--accent-color) !important;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
/* 禁用狀態 */
|
| 195 |
+
button:disabled,
|
| 196 |
+
input:disabled,
|
| 197 |
+
select:disabled {
|
| 198 |
+
opacity: 0.5;
|
| 199 |
+
cursor: not-allowed;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
/* 無障礙 - 焦點可視化 */
|
| 203 |
+
:focus-visible {
|
| 204 |
+
outline: 2px solid var(--accent-color);
|
| 205 |
+
outline-offset: 2px;
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
/* 平滑過渡 */
|
| 209 |
+
* {
|
| 210 |
+
transition: background-color var(--transition-duration),
|
| 211 |
+
color var(--transition-duration),
|
| 212 |
+
border-color var(--transition-duration);
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
/* 尊重系統深色模式偏好 */
|
| 216 |
+
@media (prefers-color-scheme: dark) {
|
| 217 |
+
:root:not([data-theme="light"]) {
|
| 218 |
+
--primary-color: #4a9eff;
|
| 219 |
+
--secondary-color: #ffb366;
|
| 220 |
+
--accent-color: #66dd88;
|
| 221 |
+
--bg-color: #1a1a1a;
|
| 222 |
+
--surface-color: #2d2d2d;
|
| 223 |
+
--text-color: #e8e8e8;
|
| 224 |
+
--text-secondary-color: #a0a0a0;
|
| 225 |
+
--border-color: #404040;
|
| 226 |
+
--hover-color: #383838;
|
| 227 |
+
--success-color: #4ade80;
|
| 228 |
+
--warning-color: #facc15;
|
| 229 |
+
--error-color: #ef4444;
|
| 230 |
+
}
|
| 231 |
+
}
|
docs/specify/plan/專案名稱/SDS.md
ADDED
|
File without changes
|
docs/specify/plan/專案名稱/SRS.md
ADDED
|
File without changes
|
docs/specify/plan/專案名稱/spec.md
ADDED
|
File without changes
|
docs/specify/plan/專案名稱/tasks.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
��Agent�ݪ�
|
docs/specify/plan/放AI產生的plan文件
ADDED
|
File without changes
|
docs/specify/templates/放AI參考的文件格式
ADDED
|
File without changes
|
openspec/changes/archive/2026-03-10-add-dark-mode/.openspec.yaml
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
schema: spec-driven
|
| 2 |
+
created: 2026-03-10
|
openspec/changes/archive/2026-03-10-add-dark-mode/design.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Context
|
| 2 |
+
|
| 3 |
+
StockRecommender 是一個基於 Gradio 的股票分析應用程式,目前僅支援亮色模式。應用程式通過 `app_batch.py` 和 `app.py` 提供用戶介面,使用 Plotly 進行互動式圖表展示。用戶在長時間交易時段(尤其是清晨或晚間)會面臨眼睛疲勞問題。本設計旨在引入深色模式支援,同時保持與現有亮色模式的向後相容性。
|
| 4 |
+
|
| 5 |
+
## Goals / Non-Goals
|
| 6 |
+
|
| 7 |
+
**Goals:**
|
| 8 |
+
- 在 Gradio UI 中提供一個直觀的主題切換控制項
|
| 9 |
+
- 儲存用戶主題選擇,確保跨會話的一致體驗
|
| 10 |
+
- 為深色模式定義完整的色彩方案,包含 Gradio 元件和 Plotly 圖表
|
| 11 |
+
- 確保深色模式下的色彩對比度符合 WCAG 標準
|
| 12 |
+
- 支援系統偏好設定自動偵測(可選功能)
|
| 13 |
+
- 確保在 Hugging Face Spaces 和本地部署中均可正常運作
|
| 14 |
+
|
| 15 |
+
**Non-Goals:**
|
| 16 |
+
- 不支援客製化主題生成或用戶自定義顏色方案
|
| 17 |
+
- 不涉及後端邏輯變更或資料模型修改
|
| 18 |
+
- 不包括其他 UI 框架的支援(僅限 Gradio)
|
| 19 |
+
|
| 20 |
+
## Decisions
|
| 21 |
+
|
| 22 |
+
### 決策 1:主題儲存方式
|
| 23 |
+
**選擇**:優先使用瀏覽器 localStorage,伺服器端預設值作為後備方案
|
| 24 |
+
|
| 25 |
+
**理由**:localStorage 提供即時、無伺服器端依賴的持久化方案,適合 Hugging Face Spaces 的無狀態環境。伺服器端預設值用於首次訪問或 localStorage 不可用時。
|
| 26 |
+
|
| 27 |
+
**考慮的替代方案**:
|
| 28 |
+
- Cookie:相比 localStorage 容量限制較多,不如優先選擇
|
| 29 |
+
- 伺服器端會話儲存:增加伺服器複雜度,不適合無狀態部署
|
| 30 |
+
|
| 31 |
+
### 決策 2:色彩方案實現方式
|
| 32 |
+
**選擇**:使用 CSS 變數 + Gradio 原生主題系統
|
| 33 |
+
|
| 34 |
+
**理由**:Gradio 3.x+ 原生支援主題切換,避免額外依賴。CSS 變數提供集中式配置,易於維護和調整。
|
| 35 |
+
|
| 36 |
+
**考慮的替代方案**:
|
| 37 |
+
- 動態 CSS 生成:複雜度高,維護困難
|
| 38 |
+
- 預定義的 CSS 類別切換:無法利用 Gradio 的主題系統
|
| 39 |
+
|
| 40 |
+
### 決策 3:Plotly 圖表主題適配
|
| 41 |
+
**選擇**:根據用戶選定的主題動態更新 Plotly 配置
|
| 42 |
+
|
| 43 |
+
**理由**:Plotly 支援主題配置參數,在圖表建立時應用適當的範本和配色。這樣無需修改已有的圖表生成邏輯。
|
| 44 |
+
|
| 45 |
+
**考慮的替代方案**:
|
| 46 |
+
- 預先定義亮/暗兩種圖表變體:冗餘且不易維護
|
| 47 |
+
- JavaScript 客戶端注入:增加複雜度
|
| 48 |
+
|
| 49 |
+
### 決策 4:主題控制項位置
|
| 50 |
+
**選擇**:在應用程式標題欄右側放置主題切換按鈕
|
| 51 |
+
|
| 52 |
+
**理由**:標題欄是用戶首先注意到的區域,右側位置符合常見的 UI 慣例。
|
| 53 |
+
|
| 54 |
+
## Risks / Trade-offs
|
| 55 |
+
|
| 56 |
+
**[風險] Plotly 圖表渲染延遲**
|
| 57 |
+
→ **風險降低措施**:在建立圖表時預先計算主題配置,避免即時轉換。
|
| 58 |
+
|
| 59 |
+
**[風險] 某些用戶瀏覽器不支援 localStorage**
|
| 60 |
+
→ **風險降低措施**:提供伺服器端預設值和優雅降級策略,默認使用亮色模式。
|
| 61 |
+
|
| 62 |
+
**[風險] 深色模式色彩對比度不足**
|
| 63 |
+
→ **風險降低措施**:使用經過驗證的色彩方案(例如參考深色模式設計指南),進行 WCAG 對比度檢查。
|
| 64 |
+
|
| 65 |
+
**[風險] Gradio 主題系統的跨版本相容性**
|
| 66 |
+
→ **風險降低措施**:在 requirements.txt 中固定 Gradio 版本,確保主題系統穩定。
|
| 67 |
+
|
| 68 |
+
**[取捨] localStorage 儲存空間有限**
|
| 69 |
+
→ 深色模式配置僅需極小的儲存空間(<1KB),此取捨可接受。
|
| 70 |
+
|
| 71 |
+
## Migration Plan
|
| 72 |
+
|
| 73 |
+
### 部署步驟
|
| 74 |
+
1. **第 1 階段**:新增主題配置文件(`theme_config.py`)和 CSS 變數定義
|
| 75 |
+
2. **第 2 階段**:修改 `app_batch.py` 和 `app.py` 以支援主題切換
|
| 76 |
+
3. **第 3 階段**:實現 localStorage 持久化邏輯
|
| 77 |
+
4. **第 4 階段**:測試 Plotly 圖表在深色模式下的正確呈現
|
| 78 |
+
5. **第 5 階段**:部署至 Hugging Face Spaces 和本地環境
|
| 79 |
+
|
| 80 |
+
### 回滾策略
|
| 81 |
+
- 若發生主題相關錯誤,用戶可清除瀏覽器 localStorage 以還原預設亮色模式
|
| 82 |
+
- 亮色模式始終作為備選方案保持可用
|
| 83 |
+
- 無需資料遷移或後端回滾
|
| 84 |
+
|
| 85 |
+
## Open Questions
|
| 86 |
+
|
| 87 |
+
1. 深色模式的預設顏色方案是否應該支援用戶的系統偏好設定偵測(`prefers-color-scheme` CSS Media Query)?
|
| 88 |
+
2. Gradio 版本升級時,主題系統的相容性如何確保?
|
| 89 |
+
3. 是否需要為深色模式提供額外的協助工具(無障礙性)測試?
|
openspec/changes/archive/2026-03-10-add-dark-mode/proposal.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 為什麼
|
| 2 |
+
|
| 3 |
+
使用股票分析工具的用戶經常在清晨或晚間交易時段的低光環境中操作。目前僅有亮色模式的介面會導致眼睛疲勞,降低可用性。新增深色模式可以改善用戶體驗、減少長時間使用時的眼睛負擔,並符合現代網頁應用程式的標準。
|
| 4 |
+
|
| 5 |
+
## 變更內容
|
| 6 |
+
|
| 7 |
+
- 在 Gradio 介面標題欄新增主題切換按鈕(亮色/深色)
|
| 8 |
+
- 定義 CSS 變數和 Gradio 深色模式主題配置,使用適當的顏色方案
|
| 9 |
+
- 將用戶的主題偏好設定儲存到瀏覽器 localStorage 或伺服器端配置
|
| 10 |
+
- 在應用程式啟動時根據用戶偏好或系統設定自動套用深色模式
|
| 11 |
+
- 更新所有 Gradio 元件(圖表、表格、輸入框)以支援主題切換
|
| 12 |
+
- 確保 Plotly 圖表在深色模式下具有足夠的色彩對比度並正確呈現
|
| 13 |
+
|
| 14 |
+
## 功能模塊
|
| 15 |
+
|
| 16 |
+
### 新增功能模塊
|
| 17 |
+
|
| 18 |
+
- `theme-toggle-ui`:提供主題切換控制項(按鈕/下拉選單),讓用戶能即時在亮色和深色模式之間切換
|
| 19 |
+
- `theme-persistence`:儲存用戶的主題偏好設定,在下次啟動應用程式時自動套用
|
| 20 |
+
- `dark-mode-styling`:提供深色模式的 CSS 和 Gradio 主題配置,確保足夠的色彩對比度和可讀性
|
| 21 |
+
- `chart-theme-support`:確保 Plotly 視覺化圖表(長條圖、散布圖、雷達圖、圓餅圖)在亮色和深色模式下均能正確呈現
|
| 22 |
+
|
| 23 |
+
### 修改現有功能模塊
|
| 24 |
+
|
| 25 |
+
<!-- 現有功能模塊的規格層級沒有變更 -->
|
| 26 |
+
|
| 27 |
+
## 影響範圍
|
| 28 |
+
|
| 29 |
+
- **修改檔案**:`app_batch.py`、`app.py`(Gradio 應用程式初始化和主題處理)
|
| 30 |
+
- **新增檔案**:`css/dark_mode.css`、`theme_config.py`(或在現有檔案中新增主題配置)
|
| 31 |
+
- **相依套件**:不需要新增外部套件(Gradio 原生支援主題功能)
|
| 32 |
+
- **API 變更**:無 API 變更;主題為客戶端/UI 層級功能
|
| 33 |
+
- **使用者體驗**:非破壞性變更;現有亮色模式保持預設值,深色模式為選擇性功能
|
| 34 |
+
- **部署支援**:支援 Hugging Face Spaces 和本地部署
|
openspec/changes/archive/2026-03-10-add-dark-mode/specs/chart-theme-support/spec.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## ADDED Requirements
|
| 2 |
+
|
| 3 |
+
### Requirement: Plotly 圖表應在深色模式下正確渲染
|
| 4 |
+
|
| 5 |
+
Plotly 視覺化圖表(包括長條圖、散布圖、雷達圖、圓餅圖)應自動適配深色模式,確保可讀性和美觀性。
|
| 6 |
+
|
| 7 |
+
#### Scenario: 長條圖在深色模式中應有清晰的對比度
|
| 8 |
+
- **WHEN** 用戶在深色模式中查看長條圖
|
| 9 |
+
- **THEN** 圖表的色彩應清晰區分,背景應與數據條形形成足夠對比
|
| 10 |
+
|
| 11 |
+
#### Scenario: 散布圖在深色模式中應清晰顯示數據點
|
| 12 |
+
- **WHEN** 用戶在深色模式中查看散布圖
|
| 13 |
+
- **THEN** 數據點的顏色應在深色背景上清晰可見,不應混淆或難以區分
|
| 14 |
+
|
| 15 |
+
#### Scenario: 雷達圖在深色模式中應保持可讀性
|
| 16 |
+
- **WHEN** 用戶在深色模式中查看雷達圖
|
| 17 |
+
- **THEN** 網格線、數據線和標籤應清晰可讀,對比度應符合標準
|
| 18 |
+
|
| 19 |
+
#### Scenario: 圓餅圖在深色模式中應清晰顯示各部分
|
| 20 |
+
- **WHEN** 用戶在深色模式中查看圓餅圖
|
| 21 |
+
- **THEN** 各部分的顏色應區分清晰,並且對比度應足以區分相鄰的片段
|
| 22 |
+
|
| 23 |
+
### Requirement: Plotly 圖表應與應用程式主題協調
|
| 24 |
+
|
| 25 |
+
圖表的背景色、文字色、網格線色等應與應用程式整體主題相協調,避免視覺不和諧。
|
| 26 |
+
|
| 27 |
+
#### Scenario: 圖表背景應匹配應用程式背景
|
| 28 |
+
- **WHEN** 主題切換時
|
| 29 |
+
- **THEN** Plotly 圖表的背景色應自動更新以匹配應用程式的深色或亮色背景
|
| 30 |
+
|
| 31 |
+
#### Scenario: 圖表文字顏色應與背景形成對比
|
| 32 |
+
- **WHEN** 用戶查看圖表
|
| 33 |
+
- **THEN** 軸標籤、圖例、標題等文字應以與背景色形成足夠對比的顏色呈現
|
| 34 |
+
|
| 35 |
+
#### Scenario: 圖表網格線應在深色模式中可見
|
| 36 |
+
- **WHEN** 用戶在深色模式中查看圖表
|
| 37 |
+
- **THEN** 網格線應以適當顏色顯示(例如淺灰色),不應過於暗淡或不可見
|
| 38 |
+
|
| 39 |
+
### Requirement: 圖表主題應無需頁面重新加載即時更新
|
| 40 |
+
|
| 41 |
+
當用戶切換主題時,已渲染的圖表應立即更新其顏色方案。
|
| 42 |
+
|
| 43 |
+
#### Scenario: 主題切換時圖表顏色應即時改變
|
| 44 |
+
- **WHEN** 用戶點擊主題切換按鈕
|
| 45 |
+
- **THEN** 當前頁面上的所有 Plotly 圖表應立即更新其色彩以反映新主題
|
| 46 |
+
|
| 47 |
+
#### Scenario: 新渲染的圖表應使用當前主題配色
|
| 48 |
+
- **WHEN** 用戶在深色模式中執行操作導致新圖表被生成
|
| 49 |
+
- **THEN** 新圖表應自動使用深色主題配色,無需額外配置
|
openspec/changes/archive/2026-03-10-add-dark-mode/specs/dark-mode-styling/spec.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## ADDED Requirements
|
| 2 |
+
|
| 3 |
+
### Requirement: 深色模式應有定義完整的顏色方案
|
| 4 |
+
|
| 5 |
+
系統應提供深色模式的完整顏色定義,包括背景、文字、邊框、輸入框等所有 UI 元件的色彩。
|
| 6 |
+
|
| 7 |
+
#### Scenario: 深色模式背景為深色且文字為淺色
|
| 8 |
+
- **WHEN** 用戶切換至深色模式
|
| 9 |
+
- **THEN** 應用程式背景應為深色(例如 #1a1a1a 或類似顏色),文字應為淺色(例如 #f0f0f0)
|
| 10 |
+
|
| 11 |
+
#### Scenario: 所有 Gradio 元件應支援深色主題
|
| 12 |
+
- **WHEN** 用戶在深色模式中使用應用程式
|
| 13 |
+
- **THEN** 所有輸入框、按鈕、下拉選單、標籤等 Gradio 元件應適配深色配色
|
| 14 |
+
|
| 15 |
+
#### Scenario: 深色模式色彩應符合 WCAG 標準
|
| 16 |
+
- **WHEN** 檢查深色模式的色彩對比度
|
| 17 |
+
- **THEN** 所有文字與背景的對比度應至少達到 WCAG AA 級別(4.5:1 對於普通文字)
|
| 18 |
+
|
| 19 |
+
### Requirement: 應使用 CSS 變數實現主題配置
|
| 20 |
+
|
| 21 |
+
顏色方案應通過 CSS 變數定義,以便於維護和修改。
|
| 22 |
+
|
| 23 |
+
#### Scenario: CSS 變數定義亮色模式顏色
|
| 24 |
+
- **WHEN** 應用程式以亮色模式運行
|
| 25 |
+
- **THEN** CSS 變數(例如 `--bg-color`, `--text-color`)應設定為亮色值
|
| 26 |
+
|
| 27 |
+
#### Scenario: CSS 變數定義深色模式顏色
|
| 28 |
+
- **WHEN** 應用程式以深色模式運行
|
| 29 |
+
- **THEN** CSS 變數應設定為深色值,所有依賴這些變數的元素應自動更新
|
| 30 |
+
|
| 31 |
+
#### Scenario: Gradio 主題配置應與 CSS 變數同步
|
| 32 |
+
- **WHEN** 主題切換發生
|
| 33 |
+
- **THEN** Gradio 的原生主題系統應與 CSS 變數保持同步,確保一致的視覺效果
|
| 34 |
+
|
| 35 |
+
### Requirement: 深色模式應包含強調色和次要色
|
| 36 |
+
|
| 37 |
+
除了背景和文字顏色外,應定義強調色、邊框色、懸停狀態色等,確保完整的視覺層次。
|
| 38 |
+
|
| 39 |
+
#### Scenario: 按鈕在深色模式中應有適當的強調色
|
| 40 |
+
- **WHEN** 用戶在深色模式中查看按鈕
|
| 41 |
+
- **THEN** 按鈕應使用與深色背景形成良好對比的強調色
|
| 42 |
+
|
| 43 |
+
#### Scenario: 互動元素(懸停、焦點)應在深色模式中可見
|
| 44 |
+
- **WHEN** 用戶在深色模式中與元件互動(懸停、焦點)
|
| 45 |
+
- **THEN** 元件的狀態變化應清晰可見,例如懸停時顏色應改變
|
openspec/changes/archive/2026-03-10-add-dark-mode/specs/theme-persistence/spec.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## ADDED Requirements
|
| 2 |
+
|
| 3 |
+
### Requirement: 用戶主題偏好應被持久化儲存
|
| 4 |
+
|
| 5 |
+
系統應將用戶選擇的主題(亮色或深色)持久化儲存,以便在用戶下次訪問應用程式時能夠恢復此偏好。
|
| 6 |
+
|
| 7 |
+
#### Scenario: 用戶選擇深色模式並關閉應用程式
|
| 8 |
+
- **WHEN** 用戶切換至深色模式並關閉瀏覽器標籤頁
|
| 9 |
+
- **THEN** 用戶的主題選擇應被儲存
|
| 10 |
+
|
| 11 |
+
#### Scenario: 用戶在新會話中恢復其主題偏好
|
| 12 |
+
- **WHEN** 用戶在新的瀏覽器會話中訪問應用程式
|
| 13 |
+
- **THEN** 應用程式應自動應用用戶先前選擇的主題(深色模式)
|
| 14 |
+
|
| 15 |
+
#### Scenario: 新用戶看到預設亮色模式
|
| 16 |
+
- **WHEN** 新用戶首次訪問應用程式且無儲存偏好
|
| 17 |
+
- **THEN** 應用程式應默認顯示亮色模式
|
| 18 |
+
|
| 19 |
+
### Requirement: 主題偏好應存儲在瀏覽器 localStorage 中
|
| 20 |
+
|
| 21 |
+
儲存機制應優先使用瀏覽器的 localStorage API 以實現跨會話持久化,並在 localStorage 不可用時提供備選方案。
|
| 22 |
+
|
| 23 |
+
#### Scenario: 應用程式使用 localStorage 儲存主題選擇
|
| 24 |
+
- **WHEN** 用戶切換主題
|
| 25 |
+
- **THEN** 系統應將主題選擇寫入瀏覽器 localStorage(例如鍵名為 `theme-preference`)
|
| 26 |
+
|
| 27 |
+
#### Scenario: 應用程式從 localStorage 讀取儲存的偏好
|
| 28 |
+
- **WHEN** 應用程式啟動
|
| 29 |
+
- **THEN** 系統應檢查 localStorage 中是否存在 `theme-preference` 鍵並相應地應用主題
|
| 30 |
+
|
| 31 |
+
#### Scenario: 當 localStorage 不可用時應有備選方案
|
| 32 |
+
- **WHEN** 瀏覽器的 localStorage 被禁用或不可用
|
| 33 |
+
- **THEN** 系統應回退至預設亮色模式,用戶仍可手動切換主題(該會話內生效)
|
| 34 |
+
|
| 35 |
+
### Requirement: 應支援系統偏好設定自動偵測(可選)
|
| 36 |
+
|
| 37 |
+
系統可以選擇性地偵測用戶的作業系統主題偏好設定(如果 localStorage 中沒有用戶選擇)。
|
| 38 |
+
|
| 39 |
+
#### Scenario: 首次訪問時偵測系統深色模式偏好
|
| 40 |
+
- **WHEN** 新用戶首次訪問且系統設定為深色模式
|
| 41 |
+
- **THEN** 應用程式可選擇自動應用深色模式(基於 CSS Media Query `prefers-color-scheme`)
|
| 42 |
+
|
| 43 |
+
#### Scenario: 用戶明確選擇覆蓋系統偏好
|
| 44 |
+
- **WHEN** 用戶手動切換主題
|
| 45 |
+
- **THEN** 用戶的選擇應儲存並優先於系統偏好
|
openspec/changes/archive/2026-03-10-add-dark-mode/specs/theme-toggle-ui/spec.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## ADDED Requirements
|
| 2 |
+
|
| 3 |
+
### Requirement: 主題切換按鈕在 UI 標題欄中可見
|
| 4 |
+
|
| 5 |
+
系統應在 Gradio 應用程式的標題欄(preferably 右側)顯示一個主題切換控制項。該控制項應直觀指示當前主題(亮或深)並允許用戶點擊以切換。
|
| 6 |
+
|
| 7 |
+
#### Scenario: 用戶在亮色模式中看到深色模式選項
|
| 8 |
+
- **WHEN** 用戶首次打開應用程式(預設亮色模式)
|
| 9 |
+
- **THEN** UI 標題欄右側應顯示一個「深色模式」按鈕或圖標
|
| 10 |
+
|
| 11 |
+
#### Scenario: 用戶點擊按鈕切換到深色模式
|
| 12 |
+
- **WHEN** 用戶點擊標題欄中的主題切換按鈕
|
| 13 |
+
- **THEN** 應用程式整個 UI 應立即切換到深色模式
|
| 14 |
+
|
| 15 |
+
#### Scenario: 用戶在深色模式中看到亮色模式選項
|
| 16 |
+
- **WHEN** 用戶已切換至深色模式
|
| 17 |
+
- **THEN** UI 標題欄中的按鈕應更新,顯示「亮色模式」選項
|
| 18 |
+
|
| 19 |
+
#### Scenario: 用戶點擊按鈕切換回亮色模式
|
| 20 |
+
- **WHEN** 用戶在深色模式中點擊主題切換按鈕
|
| 21 |
+
- **THEN** 應用程式整個 UI 應立即切換回亮色模式
|
| 22 |
+
|
| 23 |
+
### Requirement: 主題切換控制項應具有可存取性
|
| 24 |
+
|
| 25 |
+
控制項應遵循網頁無障礙指南(WCAG),包括適當的 ARIA 標籤、鍵盤導航支援和對比度要求。
|
| 26 |
+
|
| 27 |
+
#### Scenario: 用戶可通過鍵盤導航至主題切換按鈕
|
| 28 |
+
- **WHEN** 用戶使用 Tab 鍵導航
|
| 29 |
+
- **THEN** 主題切換按鈕應在標籤順序中且可通過 Enter/Space 啟動
|
| 30 |
+
|
| 31 |
+
#### Scenario: 螢幕閱讀器用戶可理解控制項的目的
|
| 32 |
+
- **WHEN** 螢幕閱讀器掃描 UI
|
| 33 |
+
- **THEN** 控制項應有清晰的 ARIA 標籤,例如「切換深色/亮色模式」
|
| 34 |
+
|
| 35 |
+
### Requirement: 主題切換應立即生效
|
| 36 |
+
|
| 37 |
+
不應有延遲或頁面重新加載。UI 應平滑地過渡至新主題。
|
| 38 |
+
|
| 39 |
+
#### Scenario: 主題切換不需要頁面重新加載
|
| 40 |
+
- **WHEN** 用戶點擊主題切換按鈕
|
| 41 |
+
- **THEN** 主題應立即改變,無需重新加載頁面
|
| 42 |
+
|
| 43 |
+
#### Scenario: 所有 UI 元素應在不刷新的情況下更新
|
| 44 |
+
- **WHEN** 主題切換發生
|
| 45 |
+
- **THEN** 所有 Gradio 元件、圖表和文字應同步更新至新主題
|
openspec/changes/archive/2026-03-10-add-dark-mode/tasks.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 1. 設置和基礎配置
|
| 2 |
+
|
| 3 |
+
- [x] 1.1 建立 `theme_config.py` 檔案,定義亮色和深色主題的顏色變數
|
| 4 |
+
- [x] 1.2 建立 `css/dark_mode.css` 檔案,使用 CSS 變數定義主題樣式
|
| 5 |
+
- [x] 1.3 在 `requirements.txt` 中確認 Gradio 版本被固定(如尚未固定)
|
| 6 |
+
|
| 7 |
+
## 2. 實現主題儲存機制
|
| 8 |
+
|
| 9 |
+
- [x] 2.1 建立 localStorage 相關的 JavaScript 程式碼,用於儲存和讀取主題偏好設定
|
| 10 |
+
- [x] 2.2 實現伺服器端預設值或備選機制,當 localStorage 不可用時使用
|
| 11 |
+
- [x] 2.3 測試主題偏好設定在跨會話中的持久化
|
| 12 |
+
|
| 13 |
+
## 3. 實現主題切換 UI 控制項
|
| 14 |
+
|
| 15 |
+
- [x] 3.1 在 `app_batch.py` 中的 Gradio 標題欄新增主題切換按鈕
|
| 16 |
+
- [x] 3.2 在 `app.py` 中的 Gradio 標題欄新增主題切換按鈕(保持一致)
|
| 17 |
+
- [x] 3.3 為按鈕新增 ARIA 標籤和鍵盤導航支援
|
| 18 |
+
- [x] 3.4 實現按鈕的點擊事件處理,觸發主題切換
|
| 19 |
+
|
| 20 |
+
## 4. 實現深色模式樣式應用
|
| 21 |
+
|
| 22 |
+
- [x] 4.1 在 `app_batch.py` 中整合 Gradio 主題配置,支援亮色和深色模式
|
| 23 |
+
- [x] 4.2 在 `app.py` 中整合 Gradio 主題配置,支援亮色和深色模式
|
| 24 |
+
- [x] 4.3 確保 CSS 變數在主題切換時正確應用到所有 UI 元件
|
| 25 |
+
- [x] 4.4 驗證所有顏色對比度符合 WCAG AA 標準
|
| 26 |
+
|
| 27 |
+
## 5. 實現 Plotly 圖表主題支援
|
| 28 |
+
|
| 29 |
+
- [x] 5.1 修改圖表生成函式,在建立時基於當前主題應用 Plotly 配置
|
| 30 |
+
- [x] 5.2 為 Plotly 定義亮色和深色兩種主題樣板
|
| 31 |
+
- [x] 5.3 實現主題切換時 Plotly 圖表的即時重新渲染
|
| 32 |
+
- [x] 5.4 在長條圖、散布圖、雷達圖、圓餅圖中測試深色模式配色
|
| 33 |
+
|
| 34 |
+
## 6. 整合應用程式啟動邏輯
|
| 35 |
+
|
| 36 |
+
- [x] 6.1 在應用程式啟動時讀取儲存的主題偏好設定
|
| 37 |
+
- [x] 6.2 實現系統偏好設定自動偵測(使用 CSS Media Query `prefers-color-scheme`)
|
| 38 |
+
- [x] 6.3 確保首次訪問用戶預設使用亮色模式
|
| 39 |
+
|
| 40 |
+
## 7. 測試和驗證
|
| 41 |
+
|
| 42 |
+
- [x] 7.1 測試主題切換按鈕的可見性和可存取性(螢幕閱讀器、鍵盤導航)
|
| 43 |
+
- [x] 7.2 測試主題偏好設定在亮色和深色模式之間的持久化
|
| 44 |
+
- [x] 7.3 測試所有 Gradio 元件在深色模式下的正確呈現
|
| 45 |
+
- [x] 7.4 測試所有 Plotly 圖表在深色模式下的顏色對比度和可讀性
|
| 46 |
+
- [x] 7.5 驗證無需頁面重新加載的即時主題切換
|
| 47 |
+
- [ ] 7.6 在 Hugging Face Spaces 環境中測試深色模式功能
|
| 48 |
+
|
| 49 |
+
## 8. 部署和文件
|
| 50 |
+
|
| 51 |
+
- [x] 8.1 更新 README 或文件,說明如何使用深色模式功能
|
| 52 |
+
- [x] 8.2 驗證深色模式在本地和 Hugging Face Spaces 中均能正常運作
|
| 53 |
+
- [x] 8.3 建立使用者回饋機制,收集深色模式的改進建議
|
openspec/config.yaml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
schema: spec-driven
|
| 2 |
+
|
| 3 |
+
# Project context
|
| 4 |
+
context: |
|
| 5 |
+
Project: StockRecommender - AI-powered stock analysis tool
|
| 6 |
+
Tech Stack: Python, Gradio (web UI), yfinance (market data), Transformers (FinBERT, BART)
|
| 7 |
+
Deployment: Hugging Face Spaces or local
|
| 8 |
+
Main Files: app_batch.py (advanced), app.py (simple), requirements.txt
|
| 9 |
+
Architecture: StockAnalyzer class for data/analysis, Plotly for visualizations
|
| 10 |
+
Conventions:
|
| 11 |
+
- Error handling: wrap external API calls (yfinance, HF) in try-except
|
| 12 |
+
- UI: Gradio components in Row/Column for layout
|
| 13 |
+
- Fallback models: use lightweight alternatives if memory constraints
|
| 14 |
+
- Runtime install: auto-install missing packages via install_package()
|
| 15 |
+
- Always update requirements.txt when adding dependencies
|
| 16 |
+
Key Patterns:
|
| 17 |
+
- ProsusAI/finbert for sentiment, facebook/bart-large-cnn for summarization
|
| 18 |
+
- Plotly for charts (Bar, Scatter, Radar, Pie)
|
| 19 |
+
- Batch analysis via text input or StockList.xlsx
|