feat(smart-mind-map-tool): initial release v1.0.0 with adaptive Rich UI

This commit is contained in:
fujie
2026-03-04 23:14:06 +08:00
parent eda495e55f
commit 56a6ddd422
36 changed files with 3550 additions and 187 deletions

71
.agent/skills/README.md Normal file
View File

@@ -0,0 +1,71 @@
# Agent Skills Index
This folder contains reusable Agent Skills for GitHub Copilot / VS Code custom agent workflows.
## Available Skills
- **community-announcer**
- Purpose: Generate community announcement content and related assets.
- Entry: `community-announcer/SKILL.md`
- **doc-mirror-sync**
- Purpose: Sync mirrored documentation content and helper scripts.
- Entry: `doc-mirror-sync/SKILL.md`
- **gh-issue-replier**
- Purpose: Draft standardized issue replies with templates.
- Entry: `gh-issue-replier/SKILL.md`
- **gh-issue-scheduler**
- Purpose: Schedule and discover unanswered issues for follow-up.
- Entry: `gh-issue-scheduler/SKILL.md`
- **i18n-validator**
- Purpose: Validate translation key consistency across i18n dictionaries.
- Entry: `i18n-validator/SKILL.md`
- **plugin-scaffolder**
- Purpose: Scaffold OpenWebUI plugin boilerplate with repository standards.
- Entry: `plugin-scaffolder/SKILL.md`
- **version-bumper**
- Purpose: Assist with semantic version bumping workflows.
- Entry: `version-bumper/SKILL.md`
- **xlsx-single-file**
- Purpose: Single-file spreadsheet operations workflow without LibreOffice.
- Entry: `xlsx-single-file/SKILL.md`
---
## Release Pipeline Skills
These four skills form a complete release pipeline and are designed to be used in sequence:
```
release-prep → pr-submitter → pr-reviewer → release-finalizer
(prepare) (push & PR) (respond to review) (merge & close issue)
```
- **release-prep**
- Purpose: Full release preparation — version sync across 7+ files, bilingual release notes creation, consistency check, and commit.
- Entry: `release-prep/SKILL.md`
- **pr-submitter**
- Purpose: Shell-escape-safe PR submission — writes body to temp file, validates sections, pushes branch, creates PR via `gh pr create --body-file`.
- Entry: `pr-submitter/SKILL.md`
- **pr-reviewer**
- Purpose: Fetch PR review comments, categorize feedback, implement fixes, commit and push, reply to reviewers.
- Entry: `pr-reviewer/SKILL.md`
- **release-finalizer**
- Purpose: Merge release PR to main with proper commit message, auto-link and close related issues, post closing messages.
- Entry: `release-finalizer/SKILL.md`
## Notes
- Skill definitions follow the expected location pattern:
- `.github/skills/<skill-name>/SKILL.md`
- Each skill may include optional `assets/`, `references/`, and `scripts/` folders.
- This directory mirrors `.gemini/skills` for compatibility.

View File

@@ -0,0 +1,23 @@
---
name: community-announcer
description: Drafts engaging English and Chinese update announcements for the OpenWebUI Community and other social platforms. Use when a new version is released.
---
# Community Announcer
## Overview
Automates the drafting of high-impact update announcements.
## Workflow
1. **Source Intel**: Read the latest version's `What's New` section from `README.md`.
2. **Drafting**: Create two versions:
- **Community Post**: Professional, structured, technical.
- **Catchy Short**: For Discord/Twitter, use emojis and bullet points.
3. **Multi-language**: Generate BOTH English and Chinese versions automatically.
## Announcement Structure (Recommended)
- **Headline**: "Update vX.X.X - [Main Feature]"
- **Introduction**: Brief context.
- **Key Highlights**: Bulleted list of fixes/features.
- **Action**: "Download from [Market Link]"
- **Closing**: Thanks and Star request.

View File

@@ -0,0 +1,50 @@
---
name: doc-mirror-sync
description: Automatically synchronizes plugin READMEs to the official documentation directory (docs/). Use after editing a plugin's local documentation to keep the MkDocs site up to date.
---
# Doc Mirror Sync
## Overview
Automates the mirroring of `plugins/{type}/{name}/README.md` to `docs/plugins/{type}/{name}.md`.
## Docs-Only Mode (No Release Changes)
Use this mode when the request is "only sync docs".
- Only update documentation mirror files under `docs/plugins/**`.
- Do **not** bump plugin version.
- Do **not** modify plugin code (`plugins/**.py`) unless explicitly requested.
- Do **not** update root badges/dates for release.
- Do **not** run release preparation steps.
## Workflow
1. Identify changed READMEs.
2. Copy content to corresponding mirror paths.
3. Update version badges in `docs/plugins/{type}/index.md`.
## Commands
### Sync all mirrors (EN + ZH)
```bash
python .github/skills/doc-mirror-sync/scripts/sync.py
```
### Sync only one plugin (EN only)
```bash
cp plugins/<type>/<name>/README.md docs/plugins/<type>/<name>.md
```
### Sync only one plugin (EN + ZH)
```bash
cp plugins/<type>/<name>/README.md docs/plugins/<type>/<name>.md
cp plugins/<type>/<name>/README_CN.md docs/plugins/<type>/<name>.zh.md
```
## Notes
- If asked for English-only update, sync only `README.md` -> `.md` mirror.
- If both languages are requested, sync both `README.md` and `README_CN.md`.
- After syncing, verify git diff only contains docs file changes.

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import os
import shutil
import re
def sync_mirrors():
plugins_root = "plugins"
docs_root = "docs/plugins"
types = ["actions", "filters", "pipes", "pipelines", "tools"]
for t in types:
src_type_dir = os.path.join(plugins_root, t)
dest_type_dir = os.path.join(docs_root, t)
if not os.path.exists(src_type_dir): continue
os.makedirs(dest_type_dir, exist_ok=True)
for name in os.listdir(src_type_dir):
plugin_dir = os.path.join(src_type_dir, name)
if not os.path.isdir(plugin_dir): continue
# Sync README.md -> docs/plugins/{type}/{name}.md
src_readme = os.path.join(plugin_dir, "README.md")
if os.path.exists(src_readme):
dest_readme = os.path.join(dest_type_dir, f"{name}.md")
shutil.copy(src_readme, dest_readme)
print(f"✅ Mirrored: {t}/{name} (EN)")
# Sync README_CN.md -> docs/plugins/{type}/{name}.zh.md
src_readme_cn = os.path.join(plugin_dir, "README_CN.md")
if os.path.exists(src_readme_cn):
dest_readme_zh = os.path.join(dest_type_dir, f"{name}.zh.md")
shutil.copy(src_readme_cn, dest_readme_zh)
print(f"✅ Mirrored: {t}/{name} (ZH)")
if __name__ == "__main__":
sync_mirrors()

View File

@@ -0,0 +1,51 @@
---
name: gh-issue-replier
description: Professional English replier for GitHub issues. Use when a task is completed, a bug is fixed, or more info is needed from the user. Automates replying using the 'gh' CLI tool.
---
# Gh Issue Replier
## Overview
The `gh-issue-replier` skill enables Gemini CLI to interact with GitHub issues professionally. It enforces English for all communications and leverages the `gh` CLI to post comments.
## Workflow
1. **Identify the Issue**: Find the issue number (e.g., #49).
2. **Check Star Status**: Run the bundled script to check if the author has starred the repo.
* Command: `bash scripts/check_star.sh <issue-number>`
* Interpretation:
* Exit code **0**: User has starred. Use "Already Starred" templates.
* Exit code **1**: User has NOT starred. Include "Star Request" in the reply.
3. **Select a Template**: Load [templates.md](references/templates.md) to choose a suitable English response pattern.
4. **Draft the Reply**: Compose a concise message based on the star status.
5. **Post the Comment**: Use the `gh` tool to submit the reply.
## Tool Integration
### Check Star Status
```bash
bash scripts/check_star.sh <issue-number>
```
### Post Comment
```bash
gh issue comment <issue-number> --body "<message-body>"
```
Example (if user has NOT starred):
```bash
gh issue comment 49 --body "This has been fixed in v1.2.7. If you find this helpful, a star on the repo would be much appreciated! ⭐"
```
Example (if user HAS starred):
```bash
gh issue comment 49 --body "This has been fixed in v1.2.7. Thanks for your support!"
```
## Guidelines
- **Language**: ALWAYS use English for the comment body, even if the system prompt or user conversation is in another language.
- **Tone**: Professional, helpful, and appreciative.
- **Precision**: When announcing a fix, mention the specific version or the logic change (e.g., "Updated regex pattern").
- **Closing**: If the issue is resolved and you have permission, you can also use `gh issue close <number>`.

View File

@@ -0,0 +1,17 @@
# Reference Documentation for Gh Issue Replier
This is a placeholder for detailed reference documentation.
Replace with actual reference content or delete if not needed.
## Structure Suggestions
### API Reference Example
- Overview
- Authentication
- Endpoints with examples
- Error codes
### Workflow Guide Example
- Prerequisites
- Step-by-step instructions
- Best practices

View File

@@ -0,0 +1,45 @@
# Issue Reply Templates
Use these templates to craft professional English replies. Adjust placeholders like `@username`, `v1.2.x`, and `[commit hash]` as needed.
## 1. Acknowledging a New Issue
Use when you first see an issue and want to let the user know you are working on it.
- "Thank you for reporting this! I'm looking into it right now."
- "Thanks for bringing this to my attention. I'll try to reproduce this behavior and get back to you shortly."
## 2. Requesting More Information
Use when you need logs or specific details to fix the bug.
- "Could you please provide the **'Original'** vs **'Normalized'** content from your browser console logs (F12)? It would help a lot in debugging."
- "It would be very helpful if you could share the specific Markdown text that triggered this issue."
## 3. Announcing a Fix
Use when you have pushed the fix to the repository.
- "This has been fixed in version **v1.2.x**. You can update the plugin to resolve it."
- "I've just pushed a fix for this in [commit hash]. Please let me know if it works for you after updating."
- "The issue was caused by a greedy regex pattern. I've updated it to use a tempered greedy token to prevent incorrect merging."
## 4. Guiding to Official Market
Always provide the official market link to ensure the user gets the latest verified version.
- "The fix is now live! You can download the latest version from the official OpenWebUI Community page here: [Plugin Market Link]. Simply update the function in your OpenWebUI instance to apply the changes."
- "I recommend getting the updated version from the official store: [Link]. It includes the fix for the spacing issue we discussed."
## 5. Closing the Issue
Use when the issue is confirmed resolved.
- "Glad to hear it's working now! Closing this for now. Feel free to reopen it if the problem persists."
- "Since this is resolved, I'm closing this issue. Thanks again for your feedback!"
## 5. Pro-tip: Star Request
Gently handle star requests based on the user's current status.
### If User has NOT starred:
- "If you find this plugin helpful, a star on the repo would be much appreciated! ⭐"
- "We'd love your support! If this fixed your issue, please consider starring the repository. ⭐"
### If User HAS already starred:
- "Thanks again for starring the project and for your continuous support!"
- "I appreciate your support and for being a stargazer of this project!"

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Robust Star Checker v2
# Usage: ./check_star.sh <issue_number>
ISSUE_NUM=$1
if [ -z "$ISSUE_NUM" ]; then exit 2; fi
# 1. Get Repo and Author info
REPO_FULL=$(gh repo view --json owner,name -q ".owner.login + \"/\" + .name")
USER_LOGIN=$(gh issue view "$ISSUE_NUM" --json author -q ".author.login")
# 2. Use GraphQL for high precision (Detects stars even when REST 404s)
IS_STARRED=$(gh api graphql -f query='
query($owner:String!, $repo:String!, $user:String!) {
repository(owner:$owner, name:$repo) {
stargazers(query:$user, first:1) {
nodes {
login
}
}
}
}' -f owner="${REPO_FULL%/*}" -f repo="${REPO_FULL#*/}" -f user="$USER_LOGIN" -q ".data.repository.stargazers.nodes[0].login")
if [ "$IS_STARRED" == "$USER_LOGIN" ]; then
echo "Confirmed: @$USER_LOGIN HAS starred $REPO_FULL. ⭐"
exit 0
else
echo "Confirmed: @$USER_LOGIN has NOT starred $REPO_FULL."
exit 1
fi

View File

@@ -0,0 +1,42 @@
---
name: gh-issue-scheduler
description: Finds all open GitHub issues that haven't been replied to by the owner, summarizes them, and generates a solution plan. Use when the user wants to audit pending tasks or plan maintenance work.
---
# Gh Issue Scheduler
## Overview
The `gh-issue-scheduler` skill helps maintainers track community feedback by identifying unaddressed issues and drafting actionable technical plans to resolve them.
## Workflow
1. **Identify Unanswered Issues**: Run the bundled script to fetch issues without owner replies.
* Command: `bash scripts/find_unanswered.sh`
2. **Analyze and Summarize**: For each identified issue, summarize the core problem and the user's intent.
3. **Generate Solution Plans**: Draft a technical "Action Plan" for each issue, including:
* **Root Cause Analysis** (if possible)
* **Proposed Fix/Implementation**
* **Verification Strategy**
4. **Present to User**: Display a structured report of all pending issues and their respective plans.
## Tool Integration
### Find Unanswered Issues
```bash
bash scripts/find_unanswered.sh
```
## Report Format
When presenting the summary, use the following Markdown structure:
### 📋 Unanswered Issues Audit
#### Issue #[Number]: [Title]
- **Author**: @username
- **Summary**: Concise description of the problem.
- **Action Plan**:
1. Step 1 (e.g., Investigate file X)
2. Step 2 (e.g., Apply fix Y)
3. Verification (e.g., Run test Z)

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Fetch all open issues and filter for those without responses from the owner/collaborators.
# Uses 'gh' CLI.
REPO_FULL=$(gh repo view --json owner,name -q ".owner.login + "/" + .name")
OWNER=${REPO_FULL%/*}
# 1. Get all open issues
OPEN_ISSUES=$(gh issue list --state open --json number,title,author,createdAt --limit 100)
echo "Analysis for repository: $REPO_FULL"
echo "------------------------------------"
# Process each issue
echo "$OPEN_ISSUES" | jq -c '.[]' | while read -r issue; do
NUMBER=$(echo "$issue" | jq -r '.number')
TITLE=$(echo "$issue" | jq -r '.title')
AUTHOR=$(echo "$issue" | jq -r '.author.login')
# Check comments for owner responses
# We look for comments where the author is the repo owner
COMMENTS=$(gh issue view "$NUMBER" --json comments -q ".comments[].author.login" 2>/dev/null)
HAS_OWNER_REPLY=false
for COMMENT_AUTHOR in $COMMENTS; do
if [ "$COMMENT_AUTHOR" == "$OWNER" ]; then
HAS_OWNER_REPLY=true
break
fi
done
if [ "$HAS_OWNER_REPLY" == "false" ]; then
echo "ISSUE_START"
echo "ID: $NUMBER"
echo "Title: $TITLE"
echo "Author: $AUTHOR"
echo "Description:"
gh issue view "$NUMBER" --json body -q ".body"
echo "ISSUE_END"
fi
done

View File

@@ -0,0 +1,14 @@
---
name: i18n-validator
description: Validates multi-language consistency in the TRANSLATIONS dictionary of a plugin. Use to check if any language keys are missing or if translations need updating.
---
# I18n Validator
## Overview
Ensures all 12 supported languages (en-US, zh-CN, etc.) have aligned translation keys.
## Features
- Detects missing keys in non-English dictionaries.
- Suggests translations using the core AI engine.
- Validates the `fallback_map` for variant redirects.

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python3
import sys
import ast
import os
def check_i18n(file_path):
if not os.path.exists(file_path):
print(f"Error: File not found {file_path}")
return
with open(file_path, 'r', encoding='utf-8') as f:
tree = ast.parse(f.read())
translations = {}
for node in tree.body:
if isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name) and target.id == "TRANSLATIONS":
translations = ast.literal_eval(node.value)
break
if not translations:
print("⚠️ No TRANSLATIONS dictionary found.")
return
# Base keys from English
base_lang = "en-US"
if base_lang not in translations:
print(f"❌ Error: {base_lang} missing in TRANSLATIONS.")
return
base_keys = set(translations[base_lang].keys())
print(f"🔍 Analyzing {file_path}...")
print(f"Standard keys ({len(base_keys)}): {', '.join(sorted(base_keys))}
")
for lang, keys in translations.items():
if lang == base_lang: continue
lang_keys = set(keys.keys())
missing = base_keys - lang_keys
extra = lang_keys - base_keys
if missing:
print(f"{lang}: Missing {len(missing)} keys: {', '.join(missing)}")
if extra:
print(f"⚠️ {lang}: Has {len(extra)} extra keys: {', '.join(extra)}")
if not missing and not extra:
print(f"{lang}: Aligned.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: validate_i18n.py <path_to_plugin.py>")
sys.exit(1)
check_i18n(sys.argv[1])

View File

@@ -0,0 +1,19 @@
---
name: plugin-scaffolder
description: Generates a standardized single-file i18n Python plugin template based on project standards. Use when starting a new plugin development to skip boilerplate writing.
---
# Plugin Scaffolder
## Overview
Generates compliant OpenWebUI plugin templates with built-in i18n, common utility methods, and required docstring fields.
## Usage
1. Provide the **Plugin Name** and **Type** (action/filter/pipe).
2. The skill will generate the `.py` file and the bilingual `README` files.
## Template Standard
- `Valves(BaseModel)` with `UPPER_SNAKE_CASE`
- `_get_user_context` with JS fallback and timeout
- `_emit_status` and `_emit_debug_log` methods
- Standardized docstring metadata

View File

@@ -0,0 +1,34 @@
# {{TITLE}}
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.1.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
{{DESCRIPTION}}
## 🔥 What's New in v0.1.0
* Initial release of {{TITLE}}.
## 🌐 Multilingual Support
Supports automatic interface and status switching for the following languages:
`English`, `简体中文`, `繁體中文 (香港)`, `繁體中文 (台灣)`, `한국어`, `日本語`, `Français`, `Deutsch`, `Español`, `Italiano`, `Tiếng Việt`, `Bahasa Indonesia`.
## ✨ Core Features
* Feature 1
* Feature 2
## How to Use 🛠️
1. Install the plugin in Open WebUI.
2. Configure settings in Valves.
## Configuration (Valves) ⚙️
| Parameter | Default | Description |
| :--- | :--- | :--- |
| `priority` | `50` | Execution priority. |
## ⭐ Support
If this plugin has been useful, a star on [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) is a big motivation for me. Thank you for the support.

View File

@@ -0,0 +1,80 @@
"""
title: {{TITLE}}
author: Fu-Jie
author_url: https://github.com/Fu-Jie/openwebui-extensions
funding_url: https://github.com/open-webui
version: 0.1.0
description: {{DESCRIPTION}}
"""
import asyncio
import logging
import json
from typing import Optional, Dict, Any, List, Callable, Awaitable
from pydantic import BaseModel, Field
from fastapi import Request
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
TRANSLATIONS = {
"en-US": {"status_starting": "Starting {{TITLE}}..."},
"zh-CN": {"status_starting": "正在启动 {{TITLE}}..."},
"zh-HK": {"status_starting": "正在啟動 {{TITLE}}..."},
"zh-TW": {"status_starting": "正在啟動 {{TITLE}}..."},
"ko-KR": {"status_starting": "{{TITLE}} 시작 중..."},
"ja-JP": {"status_starting": "{{TITLE}} を起動中..."},
"fr-FR": {"status_starting": "Démarrage de {{TITLE}}..."},
"de-DE": {"status_starting": "{{TITLE}} wird gestartet..."},
"es-ES": {"status_starting": "Iniciando {{TITLE}}..."},
"it-IT": {"status_starting": "Avvio di {{TITLE}}..."},
"vi-VN": {"status_starting": "Đang khởi động {{TITLE}}..."},
"id-ID": {"status_starting": "Memulai {{TITLE}}..."},
}
class {{CLASS_NAME}}:
class Valves(BaseModel):
priority: int = Field(default=50, description="Priority level (lower = earlier).")
show_status: bool = Field(default=True, description="Show status updates in UI.")
def __init__(self):
self.valves = self.Valves()
self.fallback_map = {
"zh": "zh-CN", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP",
"fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT",
"vi": "vi-VN", "id": "id-ID"
}
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
target_lang = lang
if target_lang not in TRANSLATIONS:
base = target_lang.split("-")[0]
target_lang = self.fallback_map.get(base, "en-US")
lang_dict = TRANSLATIONS.get(target_lang, TRANSLATIONS["en-US"])
text = lang_dict.get(key, TRANSLATIONS["en-US"].get(key, key))
return text.format(**kwargs) if kwargs else text
async def _get_user_context(self, __user__: Optional[dict], __event_call__: Optional[Callable] = None, __request__: Optional[Request] = None) -> dict:
user_data = __user__ if isinstance(__user__, dict) else {}
user_language = user_data.get("language", "en-US")
if __event_call__:
try:
js = "try { return (document.documentElement.lang || localStorage.getItem('locale') || navigator.language || 'en-US'); } catch (e) { return 'en-US'; }"
frontend_lang = await asyncio.wait_for(__event_call__({"type": "execute", "data": {"code": js}}), timeout=2.0)
if frontend_lang: user_language = frontend_lang
except: pass
return {"user_language": user_language}
async def {{METHOD_NAME}}(self, body: dict, __user__: Optional[dict] = None, __event_emitter__=None, __event_call__=None, __request__: Optional[Request] = None) -> dict:
if self.valves.show_status and __event_emitter__:
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
msg = self._get_translation(user_ctx["user_language"], "status_starting")
await __event_emitter__({"type": "status", "data": {"description": msg, "done": False}})
# Implement core logic here
if self.valves.show_status and __event_emitter__:
await __event_emitter__({"type": "status", "data": {"description": "Done", "done": True}})
return body

View File

@@ -0,0 +1,80 @@
"""
title: {{TITLE}}
author: Fu-Jie
author_url: https://github.com/Fu-Jie/openwebui-extensions
funding_url: https://github.com/open-webui
version: 0.1.0
description: {{DESCRIPTION}}
"""
import asyncio
import logging
import json
from typing import Optional, Dict, Any, List, Callable, Awaitable
from pydantic import BaseModel, Field
from fastapi import Request
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
TRANSLATIONS = {
"en-US": {"status_starting": "Starting {{TITLE}}..."},
"zh-CN": {"status_starting": "正在启动 {{TITLE}}..."},
"zh-HK": {"status_starting": "正在啟動 {{TITLE}}..."},
"zh-TW": {"status_starting": "正在啟動 {{TITLE}}..."},
"ko-KR": {"status_starting": "{{TITLE}} 시작 중..."},
"ja-JP": {"status_starting": "{{TITLE}} を起動中..."},
"fr-FR": {"status_starting": "Démarrage de {{TITLE}}..."},
"de-DE": {"status_starting": "{{TITLE}} wird gestartet..."},
"es-ES": {"status_starting": "Iniciando {{TITLE}}..."},
"it-IT": {"status_starting": "Avvio di {{TITLE}}..."},
"vi-VN": {"status_starting": "Đang khởi động {{TITLE}}..."},
"id-ID": {"status_starting": "Memulai {{TITLE}}..."},
}
class {{CLASS_NAME}}:
class Valves(BaseModel):
priority: int = Field(default=50, description="Priority level (lower = earlier).")
show_status: bool = Field(default=True, description="Show status updates in UI.")
def __init__(self):
self.valves = self.Valves()
self.fallback_map = {
"zh": "zh-CN", "en": "en-US", "ko": "ko-KR", "ja": "ja-JP",
"fr": "fr-FR", "de": "de-DE", "es": "es-ES", "it": "it-IT",
"vi": "vi-VN", "id": "id-ID"
}
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
target_lang = lang
if target_lang not in TRANSLATIONS:
base = target_lang.split("-")[0]
target_lang = self.fallback_map.get(base, "en-US")
lang_dict = TRANSLATIONS.get(target_lang, TRANSLATIONS["en-US"])
text = lang_dict.get(key, TRANSLATIONS["en-US"].get(key, key))
return text.format(**kwargs) if kwargs else text
async def _get_user_context(self, __user__: Optional[dict], __event_call__: Optional[Callable] = None, __request__: Optional[Request] = None) -> dict:
user_data = __user__ if isinstance(__user__, dict) else {}
user_language = user_data.get("language", "en-US")
if __event_call__:
try:
js = "try { return (document.documentElement.lang || localStorage.getItem('locale') || navigator.language || 'en-US'); } catch (e) { return 'en-US'; }"
frontend_lang = await asyncio.wait_for(__event_call__({"type": "execute", "data": {"code": js}}), timeout=2.0)
if frontend_lang: user_language = frontend_lang
except: pass
return {"user_language": user_language}
async def {{METHOD_NAME}}(self, body: dict, __user__: Optional[dict] = None, __event_emitter__=None, __event_call__=None, __request__: Optional[Request] = None) -> dict:
if self.valves.show_status and __event_emitter__:
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
msg = self._get_translation(user_ctx["user_language"], "status_starting")
await __event_emitter__({"type": "status", "data": {"description": msg, "done": False}})
# Implement core logic here
if self.valves.show_status and __event_emitter__:
await __event_emitter__({"type": "status", "data": {"description": "Done", "done": True}})
return body

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env python3
import sys
import os
def scaffold(p_type, p_name, title, desc):
target_dir = f"plugins/{p_type}/{p_name}"
os.makedirs(target_dir, exist_ok=True)
class_name = (
"Action"
if p_type == "actions"
else (
"Filter"
if p_type == "filters"
else "Tools" if p_type == "tools" else "Pipe"
)
)
method_name = (
"action"
if p_type == "actions"
else (
"outlet"
if p_type == "filters"
else "execute" if p_type == "tools" else "pipe"
)
)
replacements = {
"{{TITLE}}": title,
"{{DESCRIPTION}}": desc,
"{{CLASS_NAME}}": class_name,
"{{METHOD_NAME}}": method_name,
}
# Files to generate
templates = [
("assets/template.py.j2", f"{p_name}.py"),
("assets/README_template.md", "README.md"),
("assets/README_template.md", "README_CN.md"),
]
# Path relative to skill root
skill_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
for t_path, t_name in templates:
template_file = os.path.join(skill_root, t_path)
if not os.path.exists(template_file):
print(f"⚠️ Warning: Template not found {template_file}")
continue
with open(template_file, "r") as f:
content = f.read()
for k, v in replacements.items():
content = content.replace(k, v)
with open(os.path.join(target_dir, t_name), "w") as f:
f.write(content)
print(f"✅ Generated: {target_dir}/{t_name}")
if __name__ == "__main__":
if len(sys.argv) < 5:
print("Usage: scaffold.py <type> <name> <title> <desc>")
sys.exit(1)
scaffold(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])

View File

@@ -0,0 +1,180 @@
---
name: pr-reviewer
description: Fetches PR review comments, analyzes requested changes, implements fixes, commits and pushes the resolution. Use after a reviewer has left comments on an open PR to close the feedback loop efficiently.
---
# PR Reviewer
## Overview
This skill automates the response cycle for code review. When a reviewer leaves comments on a Pull Request, this skill fetches all pending feedback, categorizes issues by severity, implements fixes, and submits a follow-up commit with appropriate review response comments.
## Prerequisites
- An open PR exists with pending review comments
- The local branch matches the PR's head branch
- `gh` CLI is authenticated
---
## Workflow
### Step 1 — Fetch Review State
Retrieve all review comments and overall review status:
```bash
# Get overall review decisions
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json reviews,reviewDecision,headRefName \
--jq '{decision: .reviewDecision, reviews: [.reviews[] | {author: .author.login, state: .state, body: .body}]}'
# Get inline code comments (specific line comments)
PAGER=cat GH_PAGER=cat gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/comments \
--jq '[.[] | {path: .path, line: .line, body: .body, author: .user.login, id: .id}]'
# Get general issue comments
PAGER=cat GH_PAGER=cat gh issue view <PR-NUMBER> --comments --json comments \
--jq '[.comments[] | {author: .author.login, body: .body}]'
```
Confirm the current local branch matches the PR head:
```bash
git branch --show-current
```
If mismatched, checkout the correct branch first.
### Step 2 — Categorize Review Feedback
Group feedback into categories:
| Category | Examples | Action |
|----------|---------|--------|
| **Code Bug** | Logic error, incorrect variable, broken condition | Fix code immediately |
| **Style / Formatting** | Indentation, naming convention, missing blank line | Fix code |
| **Documentation** | Missing i18n key, wrong version in README, typo | Fix docs |
| **Design Question** | Suggestion to restructure, alternative approach | Discuss with user before implementing |
| **Nitpick / Optional** | Minor style preferences reviewer marked as optional | Fix if quick; document if skipped |
| **Blocking** | Reviewer explicitly blocks merge | Must fix before proceeding |
Present the full categorized list to the user and confirm the resolution plan.
### Step 3 — Implement Fixes
For each accepted fix:
1. Read the affected file at the commented line for context:
```bash
sed -n '<line-5>,<line+10>p' <file-path>
```
2. Apply the fix using appropriate file edit tools
3. After editing, verify the specific area looks correct
**For code changes that might affect behavior:**
- Check if tests exist: `ls tests/test_*.py`
- If tests exist, run them: `python -m pytest tests/ -v`
**For documentation fixes:**
- If modifying README.md, check if `docs/` mirror needs the same fix
- Apply the same fix to both locations
### Step 4 — Run Consistency Checks
After all fixes are applied:
```bash
# Version consistency (if any version files were touched)
python3 scripts/check_version_consistency.py
# Quick syntax check for Python files
python3 -m py_compile plugins/{type}/{name}/{name}.py && echo "✅ Syntax OK"
```
### Step 5 — Stage and Commit
Create a new commit (do NOT amend if the branch has already been pushed, to avoid force-push):
```bash
git add -A
git status
```
Draft a Conventional Commits message for the fixup:
Format: `fix(scope): address review feedback`
Body should list what was fixed, referencing reviewer concerns:
```
fix(github-copilot-sdk): address review feedback from @reviewer
- Fix X per review comment on line Y of file Z
- Update README to clarify auth requirement
- Correct edge case in _parse_mcp_servers logic
```
```bash
git commit -m "<fixup commit message>"
```
### Step 6 — Push the Fix Commit
```bash
git push origin $(git branch --show-current)
```
**Force-push policy:**
- Use `git push` (non-force) by default
- Only use `git push --force-with-lease` if:
1. The user explicitly requests it, AND
2. The only change is an amended commit squash (cosmetic, no logic change)
3. Never use `--force` (without `--lease`)
### Step 7 — Respond to Reviewers
For each addressed review comment, post a reply:
```bash
# Reply to inline comment
gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/comments/<COMMENT-ID>/replies \
-X POST -f body="Fixed in commit <SHORT-SHA>. <Brief explanation of what was changed.>"
# General comment to summarize all fixes
gh issue comment <PR-NUMBER> --body "All review feedback addressed in commit <SHORT-SHA>:
- Fixed: <item 1>
- Fixed: <item 2>
Ready for re-review. 🙏"
```
### Step 8 — Re-Request Review (Optional)
If the reviewer had submitted a `CHANGES_REQUESTED` review, request a new review after fixes:
```bash
PAGER=cat GH_PAGER=cat gh api repos/Fu-Jie/openwebui-extensions/pulls/<PR-NUMBER>/requested_reviewers \
-X POST -f reviewers[]='<reviewer-login>'
```
---
## Decision Guide
### When NOT to implement a suggestion immediately
- **Design questions**: "Should this be a separate class?" — Present to user for decision
- **Optional nitpicks**: Reviewer marked as `nit:` — Ask user if they want to include it
- **Large refactors**: If fix would require changing >50 lines, propose a separate follow-up issue instead
### When to ask the user before proceeding
- Any fix involving behavioral changes to plugin logic
- Renaming Valve keys (breaking change — requires migration notes)
- Changes that affect the bilingual release notes already committed
---
## Anti-Patterns to Avoid
- ❌ Do NOT `git commit --amend` on a pushed commit without user approval for force-push
- ❌ Do NOT silently skip a reviewer's comment; always acknowledge it (implement or explain why not)
- ❌ Do NOT use `--force` (only `--force-with-lease` when absolutely necessary)
- ❌ Do NOT make unrelated changes in the fixup commit; keep scope focused on review feedback
- ❌ Do NOT respond to reviewer comments in Chinese if the PR language context is English

View File

@@ -0,0 +1,194 @@
---
name: pr-submitter
description: Submits a feature branch as a Pull Request with a validated, properly formatted bilingual PR body. Handles shell-escape-safe body writing via temp files. Use after release-prep has committed all changes.
---
# PR Submitter
## Overview
This skill handles the final step of pushing a feature branch and creating a validated Pull Request on GitHub. Its primary purpose is to avoid the shell-escaping pitfalls (backticks, special characters in `gh pr create --body`) by always writing the PR body to a **temp file** first.
## Prerequisites
- All changes are committed (use `release-prep` skill first)
- The `gh` CLI is authenticated (`gh auth status`)
- Current branch is NOT `main` or `master`
---
## Workflow
### Step 0 — Initialize Temp Directory (Project-Based)
For all temporary files, use the project's `.temp/` directory instead of system `/tmp`:
```bash
# Create temp directory if it doesn't exist
mkdir -p .temp
```
**Why**: All temporary files stay within the project workspace, avoiding system `/tmp` pollution and better aligning with OpenWebUI workspace isolation principles.
### Step 1 — Pre-Flight Checks
Run these checks before any push:
```bash
# 1. Confirm not on protected branch
git branch --show-current
# 2. Verify there are commits to push
git log origin/$(git branch --show-current)..HEAD --oneline 2>/dev/null || echo "No remote tracking branch yet"
# 3. Check gh CLI auth
gh auth status
```
If any check fails, stop and report clearly.
### Step 2 — Collect PR Metadata
Gather:
- **PR Title**: Must follow Conventional Commits format, English only (e.g., `feat(github-copilot-sdk): release v0.8.0 with conditional tool filtering`)
- **Target base branch**: Default is `main`
- **Plugin name + version** (to build body sections)
- **Key changes** (reuse from release-prep or the latest What's New section)
### Step 3 — Build PR Body File (Shell-Escape-Safe)
**Always write the body to a temp file in `.temp/` directory.** Never embed multi-line markdown with special characters directly in a shell command.
```bash
cat > .temp/pr_body.md << 'HEREDOC'
## Summary
Brief one-sentence description of what this PR accomplishes.
## Changes
### New Features
- Feature 1 description
- Feature 2 description
### Bug Fixes
- Fix 1 description
## Plugin Version
- `PluginName` bumped to `vX.X.X`
## Documentation
- README.md / README_CN.md updated
- docs/ mirrors synced
## Testing
- [ ] Tested locally in OpenWebUI
- [ ] i18n validated (all language keys present)
- [ ] Version consistency check passed (`python3 scripts/check_version_consistency.py`)
---
## 变更摘要(中文)
简要描述本次 PR 的改动内容。
### 新功能
- 功能1描述
- 功能2描述
### 问题修复
- 修复1描述
HEREDOC
```
**Critical rules for the body file:**
- Use `<< 'HEREDOC'` (quoted heredoc) to prevent variable expansion
- Keep all backticks literal — they are safe inside a heredoc
- Paths like `/api/v1/files/` are safe too since heredoc doesn't interpret them as commands
### Step 4 — Validate PR Body
Before submitting, verify the body file contains expected sections:
```bash
# Check key sections exist
grep -q "## Summary" .temp/pr_body.md && echo "✅ Summary" || echo "❌ Summary missing"
grep -q "## Changes" .temp/pr_body.md && echo "✅ Changes" || echo "❌ Changes missing"
grep -q "## 变更摘要" .temp/pr_body.md && echo "✅ CN Section" || echo "❌ CN Section missing"
# Preview the body
cat .temp/pr_body.md
```
Ask the user to confirm the body content before proceeding.
### Step 5 — Push Branch
```bash
git push -u origin $(git branch --show-current)
```
If push is rejected (non-fast-forward), report to user and ask whether to force-push. **Do NOT force-push without explicit confirmation.**
### Step 6 — Create Pull Request
```bash
gh pr create \
--base main \
--head $(git branch --show-current) \
--title "<PR title from Step 2>" \
--body-file .temp/pr_body.md
```
Always use `--body-file`, never `--body` with inline markdown.
### Step 7 — Verify PR Creation
```bash
PAGER=cat GH_PAGER=cat gh pr view --json number,url,title,body --jq '{number: .number, url: .url, title: .title, body_preview: .body[:200]}'
```
Confirm:
- PR number and URL
- Title matches intended Conventional Commits format
- Body preview includes key sections (not truncated/corrupted)
If the body appears corrupted (empty sections, missing backtick content), use edit:
```bash
gh pr edit <PR-NUMBER> --body-file /tmp/pr_body.md
```
### Step 8 — Cleanup
```bash
rm -f .temp/pr_body.md
```
**Note**: The `.temp/` directory itself is preserved for reuse; only the individual PR body file is deleted. To fully clean up: `rm -rf .temp/`
Report final PR URL to the user.
---
## Shell-Escape Safety Rules
| Risk | Safe Approach |
|------|--------------|
| Backticks in `--body` | Write to file, use `--body-file` |
| Paths like `/api/...` | Safe in heredoc; risky in inline `--body` |
| Newlines in `--body` | File-based only |
| `$variable` expansion | Use `<< 'HEREDOC'` (quoted) |
| Double quotes in body | Safe in heredoc file |
| Temp file storage | Use `.temp/` dir, not `/tmp` |
| Cleanup after use | Always delete temp file (keep dir) |
---
## Anti-Patterns to Avoid
- ❌ Never use `--body "..."` with multi-line content directly in shell command
- ❌ Never interpolate variables directly into heredoc without quoting the delimiter
- ❌ Never force-push (`--force`) without explicit user confirmation
- ❌ Never target `main` as the source branch (only as base)
- ❌ Never skip the body validation step — a PR with empty body is worse than a delayed PR

View File

@@ -0,0 +1,208 @@
---
name: release-finalizer
description: Merges a release PR, associates it with resolved issues, replies to issue reporters, and closes issues. Use after PR review is complete and ready for merge. Closes the release cycle.
---
# Release Finalizer
## Overview
This skill completes the final step of the release cycle: merging the release PR to `main`, replying to all related issues with solutions, and automatically closing them using GitHub's issue linking mechanism.
## Prerequisites
- The PR is in `OPEN` state and ready to merge
- All status checks have passed (CI green)
- All review feedback has been addressed
- The PR relates to one or more GitHub issues (either in PR description or through commits)
---
## Workflow
### Step 1 — Pre-Merge Verification
Verify that the PR is ready:
```bash
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json state,statusCheckRollup,reviewDecision
```
Checklist:
-`state` is `OPEN`
-`statusCheckRollup` all have `conclusion: SUCCESS`
-`reviewDecision` is `APPROVED` or empty (no blocking reviews)
If any check fails, **do NOT merge**. Report the issue to the user.
### Step 2 — Identify Related Issues
Issues can be linked to a PR in multiple ways. Check the PR description and commit messages for keywords:
```bash
PAGER=cat GH_PAGER=cat gh pr view <PR-NUMBER> --json body,commits
```
Look for patterns like:
- `Closes #XX`, `Fixes #XX`, `Resolves #XX` (in description or commit bodies)
- `#XX` mentioned as "related to" or "addresses"
**Manual input**: If issue links are not in the PR, ask the user which issue(s) this PR resolves.
Extract all issue numbers into a list: `[#48, #52, ...]`
### Step 3 — Select Merge Strategy
Offer the user three options:
| Strategy | Git Behavior | Use Case |
|----------|-------------|----------|
| **Squash** | All commits squashed into one commit on main | Clean history, recommended for release PRs |
| **Rebase** | Linear history, no merge commit | Preserve commit granularity |
| **Merge** | Merge commit created | Preserve full PR context |
**Recommendation for release PRs**: Use `--squash` to create a single clean commit.
If user doesn't specify, default to `--squash`.
### Step 4 — Prepare Merge Commit Message
If using `--squash`, craft a single comprehensive commit message:
**Format** (Conventional Commits + Github linking):
```
type(scope): description
- Bullet point 1
- Bullet point 2
Closes #48
Closes #52
```
The `Closes #XX` keyword tells GitHub to automatically close those issues when the commit lands on `main`.
Example:
```
feat(pipes,filters): release Copilot SDK Pipe v0.8.0 and Files Filter v0.1.3
- Implement P1~P4 conditional tool filtering system
- Fix file publishing reliability across all storage backends
- Add strict file URL validation
- Update bilingual documentation
Closes #48
```
### Step 5 — Execute Merge
```bash
gh pr merge <PR-NUMBER> \
--squash \
--delete-branch \
-m "type(scope): description" \
-b "- Bullet 1\n- Bullet 2\n\nCloses #48"
```
**Key flags:**
- `--squash`: Squash commits (recommended for releases)
- `--delete-branch`: Delete the feature branch after merge
- `-m`: Commit subject
- `-b`: Commit body (supports `\n` for newlines)
Confirm the merge is successful; GitHub will automatically close related issues with `Closes #XX` keyword.
### Step 6 — Verify Auto-Close
GitHub automatically closes issues when a commit with `Closes #XX` lands on the default branch (`main`).
To verify:
```bash
PAGER=cat GH_PAGER=cat gh issue view <ISSUE-NUMBER> --json state
```
Should show `state: CLOSED`.
### Step 7 — Post Closing Message (Optional but Recommended)
For better UX, manually post a summary comment to **each issue** before it auto-closes (since auto-close happens silently):
```bash
gh issue comment <ISSUE-NUMBER> --body "
This has been fixed in PR #<PR-NUMBER>, which is now merged to main.
**Solution Summary:**
- <Key fix 1>
- <Key fix 2>
The fix will be available in the next plugin release. Thank you for reporting! ⭐
"
```
### Step 8 — (Optional) Regenerate Release Notes
If the merge revealed any final tweaks to release notes:
```bash
# Re-export release notes from merged commit
git log --oneline -1 <merged-commit-sha>
```
If needed, create a follow-up PR with doc polish (do NOT force-push the merged commit).
---
## Merge Strategy Decision Tree
```
Is this a patch/hotfix release?
├─ YES → Use --squash
└─ NO → Multi-feature release?
├─ YES → Use --squash (cleaner history)
└─ NO → Preserve detail?
├─ YES → Use --rebase
└─ NO → Use --merge (preserve PR context)
```
---
## Issue Auto-Close Keywords
These keywords in commit/PR messages will auto-close issues when merged to `main`:
- `Closes #XX`
- `Fixes #XX`
- `Resolves #XX`
- `close #XX` (case-insensitive)
- `fix #XX`
- `resolve #XX`
**Important**: The keyword must be on the **final commit that lands on** `main`. For squash merges, it must be in the squash commit message body.
---
## Anti-Patterns to Avoid
- ❌ Do NOT merge if any status checks are PENDING or FAILED
- ❌ Do NOT merge if there are blocking reviews (reviewDecision: `CHANGES_REQUESTED`)
- ❌ Do NOT merge without verifying the Conventional Commits format in the merge message
- ❌ Do NOT merge without including `Closes #XX` keywords for all related issues
- ❌ Do NOT assume issues will auto-close silently — post a courtesy comment first
- ❌ Do NOT delete the branch if it might be needed for cherry-pick or hotfixes later
---
## Troubleshooting
### Issue did not auto-close after merge
- Verify the `Closes #XX` keyword is in the **final commit message** (use `git log` to check)
- Ensure the commit is on the `main` branch
- GitHub sometimes takes a few seconds to process; refresh the issue page
### Multiple issues to close
- List all in separate `Closes #XX` lines in the commit body
- Each one will be independently auto-closed
### Want to close issue without merge?
- Use `gh issue close <ISSUE-NUMBER>` manually
- Only recommended if the PR was manually reverted or deemed invalid

View File

@@ -0,0 +1,157 @@
---
name: release-prep
description: Orchestrates the full release preparation flow for a plugin — version sync across 7+ files, bilingual release notes creation, and commit message drafting. Use before submitting a PR. Does NOT push or create a PR; that is handled by pr-submitter.
---
# Release Prep
## Overview
This skill drives the complete pre-PR release pipeline. It enforces the repository rule that every release must synchronize the version number and changelog across **at least 7 locations** before a commit is created.
## Scope
This skill covers:
1. Version sync (delegates detail to `version-bumper` if needed)
2. Bilingual release notes file creation
3. 7-location consistency verification
4. Conventional Commits message drafting
5. `git add -A && git commit` execution
It **stops before** `git push` or `gh pr create`. Use the `pr-submitter` skill for those steps.
### Temporary File Convention
Any temporary files created during release prep (e.g., draft changelogs) must:
- Be written to the project's `.temp/` directory, **NOT** system `/tmp`
- Be cleaned up before commit using `rm -f .temp/file_name`
- Never be committed to git (add `.temp/` to `.gitignore`)
---
## Workflow
### Step 1 — Collect Release Info
Ask the user (or infer from current state) the following:
- **Plugin name** and **type** (actions / filters / pipes / tools)
- **New version number** (e.g., `0.8.0`)
- **Key changes** in English and Chinese (1-5 bullet points each)
If a `What's New` section already exists in README.md, extract it as the source of truth.
### Step 2 — Sync Version Across 7 Locations
Verify AND update the version string in all of the following. Mark each as ✅ or ❌:
| # | File | Location |
|---|------|----------|
| 1 | `plugins/{type}/{name}/{name}.py` | `version:` in docstring |
| 2 | `plugins/{type}/{name}/README.md` | `**Version:** x.x.x` metadata line |
| 3 | `plugins/{type}/{name}/README_CN.md` | `**Version:** x.x.x` metadata line |
| 4 | `docs/plugins/{type}/{name}.md` | `**Version:** x.x.x` metadata line |
| 5 | `docs/plugins/{type}/{name}.zh.md` | `**Version:** x.x.x` metadata line |
| 6 | `docs/plugins/{type}/index.md` | version badge for this plugin |
| 7 | `docs/plugins/{type}/index.zh.md` | version badge for this plugin |
Additionally update the root-level **updated date badge** in:
- `README.md``![updated](https://img.shields.io/badge/YYYY--MM--DD-gray?style=flat)`
- `README_CN.md` — same badge format
Use today's date (`YYYY-MM-DD`) for the badge.
### Step 3 — Update What's New (All 4 Doc Files)
The `What's New` / `最新更新` section must contain **only the most recent release's changes**. Previous entries should be removed from this section (they live in CHANGELOG or release notes files).
Update these 4 files' `What's New` / `最新更新` block consistently:
- `plugins/{type}/{name}/README.md`
- `plugins/{type}/{name}/README_CN.md`
- `docs/plugins/{type}/{name}.md`
- `docs/plugins/{type}/{name}.zh.md`
### Step 4 — Create Bilingual Release Notes Files
Create two versioned release notes files:
**Path**: `plugins/{type}/{name}/v{version}.md`
**Path**: `plugins/{type}/{name}/v{version}_CN.md`
#### Required Sections
Each file must include:
1. **Title**: `# v{version} Release Notes` (EN) / `# v{version} 版本发布说明` (CN)
2. **Overview**: One paragraph summarizing this release
3. **New Features** / **新功能**: Bulleted list of features
4. **Bug Fixes** / **问题修复**: Bulleted list of fixes
5. **Migration Notes** / **迁移说明**: Breaking changes or Valve key renames (omit section if none)
6. **Companion Plugins** / **配套插件** (optional): If a companion plugin was updated
If a release notes file already exists for this version, update it rather than creating a new one.
#### Full Coverage Rule (Mandatory)
Release notes must cover **all updates in the current release scope** and not only headline features.
Minimum required coverage in both EN/CN files:
- New features and capability enhancements
- Bug fixes and reliability fixes
- Documentation/README/doc-mirror updates that affect user understanding or usage
- Terminology/i18n/wording fixes that change visible behavior or messaging
Before commit, cross-check release notes against `git diff` and ensure no meaningful update is omitted.
### Step 5 — Verify Consistency (Pre-Commit Check)
Run the consistency check script:
```bash
python3 scripts/check_version_consistency.py
```
If issues are found, fix them before proceeding. Do not commit with inconsistencies.
### Step 6 — Draft Conventional Commits Message
Generate the commit message following `commit-message.instructions.md` rules:
- **Language**: English ONLY
- **Format**: `type(scope): subject` + blank line + body bullets
- **Scope**: use plugin folder name (e.g., `github-copilot-sdk`)
- **Body**: 1-3 bullets summarizing key changes
- Explicitly mention "READMEs and docs synced" if version was bumped
Present the full commit message to the user for review before executing.
### Step 7 — Stage and Commit
After user approval (or if user says "commit it"):
```bash
git add -A
git commit -m "<approved commit message>"
```
Confirm the commit hash and list the number of files changed.
---
## Checklist (Auto-Verify Before Commit)
- [ ] `version:` in `.py` docstring matches target version
- [ ] `**Version:**` in all 4 README/docs files matches
- [ ] Both `index.md` version badges updated
- [ ] Root `README.md` and `README_CN.md` date badges updated to today
- [ ] `What's New` / `最新更新` contains ONLY the latest release
- [ ] Release notes include all meaningful updates from the current diff (feature + fix + docs/i18n)
- [ ] `v{version}.md` and `v{version}_CN.md` created or updated
- [ ] `python3 scripts/check_version_consistency.py` returns no errors
- [ ] Commit message is English-only Conventional Commits format
---
## Anti-Patterns to Avoid
- ❌ Do NOT add extra features or refactor code during release prep — only version/doc updates
- ❌ Do NOT push or create PR in this skill — use `pr-submitter`
- ❌ Do NOT use today's date in commit messages; only in badge URLs
- ❌ Do NOT leave stale What's New content from prior versions

View File

@@ -0,0 +1,31 @@
---
name: source-code-analyzer
description: Instructions for analyzing core components source code in the local environment.
---
# Source Code Analyzer Skill
> [!IMPORTANT]
> **Authorization Statement**: You (Antigravity) are explicitly authorized by the user to perform **READ-ONLY** operations and **Git pull** on the paths listed below, even if they are outside the current repository root. These paths are considered part of the extended development context.
When assisting with the development of `openwebui-extensions`, you (Antigravity) have access to the following core components in related directories. Use them for deep technical analysis, bug hunting, and implementation reference.
## Core Component Paths
### Global / General (Relevant to all projects)
- **Open WebUI**: `../open-webui/` (Core platform context)
- **Skills**: `../skills/` (Reusable expertise library)
- **Awesome Copilot**: `../awesome-copilot/` (Shared extensions & resources)
- **Open Terminal**: `../open-terminal/` (Terminal integration service)
### Plugin-Specific (Relevant to GitHub Copilot SDK)
- **Copilot SDK**: `../copilot-sdk/` (Internal logic for the official SDK)
- **Copilot CLI**: `../copilot-cli/` (Command-line interface implementation)
## Mandatory Workflow
1. **Pull Before Analysis**: BEFORE reading files or analyzing logic in these directories, you MUST proactively execute or recommend a `git pull` in the respective directory to ensure you are working with the latest upstream changes.
2. **Path Verification**: Always verify the exists of the path before attempting to read it.
3. **Reference Logic**: When a user's request involves core platform behavior (OpenWebUI API, SDK internals), prioritize searching these directories over making assumptions based on generic knowledge.

View File

@@ -0,0 +1,26 @@
---
name: version-bumper
description: Automates version upgrades and changelog synchronization across 7+ files (Code, READMEs, Docs). Use when a plugin is ready for release to ensure version consistency.
---
# Version Bumper
## Overview
This skill ensures that every version upgrade is synchronized across the entire repository, following the strict "Documentation Sync" rule in GEMINI.md.
## Workflow
1. **Prepare Info**: Gather the new version number and brief changelogs in both English and Chinese.
2. **Auto-Patch**: The skill will help you identify and update:
- `plugins/.../name.py` (docstring version)
- `plugins/.../README.md` (metadata & What's New)
- `plugins/.../README_CN.md` (metadata & 最新更新)
- `docs/plugins/...md` (mirrors)
- `docs/plugins/index.md` (version badge)
- `README.md` (updated date badge)
3. **Verify**: Check the diffs to ensure no formatting was broken.
## Tool Integration
Execute the bump script (draft):
```bash
python3 scripts/bump.py <version> "<message_en>" "<message_zh>"
```

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
import sys
import os
import re
from datetime import datetime
def patch_file(file_path, old_pattern, new_content, is_regex=False):
if not os.path.exists(file_path):
print(f"Warning: File not found: {file_path}")
return False
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
if is_regex:
new_content_result = re.sub(old_pattern, new_content, content, flags=re.MULTILINE)
else:
new_content_result = content.replace(old_pattern, new_content)
if new_content_result != content:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(new_content_result)
print(f"✅ Patched: {file_path}")
return True
else:
print(f" No change needed: {file_path}")
return False
def bump_version(plugin_type, plugin_name, new_version, msg_en, msg_zh):
print(f"🚀 Bumping {plugin_name} ({plugin_type}) to {new_version}...")
today = datetime.now().strftime("%Y-%m-%d")
today_badge = today.replace("-", "--")
# 1. Patch Plugin Python File
py_file = f"plugins/{plugin_type}/{plugin_name}/{plugin_name}.py"
patch_file(py_file, r"version: \d+\.\d+\.\d+", f"version: {new_version}", is_regex=True)
# 2. Patch Plugin READMEs
readme_en = f"plugins/{plugin_type}/{plugin_name}/README.md"
readme_zh = f"plugins/{plugin_type}/{plugin_name}/README_CN.md"
# Update version in metadata
patch_file(readme_en, r"\*\*Version:\*\* \d+\.\d+\.\d+", f"**Version:** {new_version}", is_regex=True)
patch_file(readme_zh, r"\*\*版本:\*\* \d+\.\d+\.\d+", f"**版本:** {new_version}", is_regex=True)
# Update What's New (Assuming standard headers)
patch_file(readme_en, r"## 🔥 What's New in v.*?\n", f"## 🔥 What's New in v{new_version}\n\n* {msg_en}\n", is_regex=True)
patch_file(readme_zh, r"## 🔥 最新更新 v.*?\n", f"## 🔥 最新更新 v{new_version}\n\n* {msg_zh}\n", is_regex=True)
# 3. Patch Docs Mirrors
doc_en = f"docs/plugins/{plugin_type}/{plugin_name}.md"
doc_zh = f"docs/plugins/{plugin_type}/{plugin_name}.zh.md"
patch_file(doc_en, r"\*\*Version:\*\* \d+\.\d+\.\d+", f"**Version:** {new_version}", is_regex=True)
patch_file(doc_zh, r"\*\*版本:\*\* \d+\.\d+\.\d+", f"**版本:** {new_version}", is_regex=True)
# 4. Patch Root READMEs (Updated Date Badge)
patch_file("README.md", r"badge/202\d--\d\d--\d\d-gray", f"badge/{today_badge}-gray", is_regex=True)
patch_file("README_CN.md", r"badge/202\d--\d\d--\d\d-gray", f"badge/{today_badge}-gray", is_regex=True)
print("\n✨ All synchronization tasks completed.")
return True
if __name__ == "__main__":
if len(sys.argv) < 6:
print("Usage: bump.py <type> <name> <version> <msg_en> <msg_zh>")
print("Example: bump.py filters markdown_normalizer 1.2.8 'Fix bug' '修复错误'")
sys.exit(1)
bump_version(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5])