refactor: 通用技能按类别拆分为独立目录

skills/ → skills-dev(9), skills-req(10), skills-ops(4),
skills-integration(8), skills-biz(4), skills-workflow(7)

generate-marketplace.py 改为自动扫描所有 skills-* 目录。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 11:31:58 +10:30
parent ea266e9cce
commit 712063071c
170 changed files with 341 additions and 346 deletions

View File

@@ -0,0 +1,8 @@
{
"name": "ai-proj-plugin",
"description": "AI project management via REST API. Works out of the box!",
"version": "2.0.1",
"author": {
"name": "qiudl"
}
}

View File

@@ -0,0 +1,136 @@
# ai-proj Plugin
AI project task and requirement management through direct REST API calls.
## Features
- **Task Management**: Create, list, update, and complete tasks
- **Requirement Management**: Full requirement lifecycle (draft → review → development → complete)
- **Documentation**: Create and manage task documents
- **Daily Focus**: Plan and view today's tasks
- **Direct API Integration**: Uses REST API directly
## Quick Start
Just install the plugin and start using it:
```bash
/plugin install ai-proj-plugin@coolbuy-claude-plugins
```
That's it! No configuration needed.
## Usage
This plugin provides **Agent Skills** that I (Claude) automatically use when you mention relevant topics.
### Examples
**Task Management:**
- "Create a task: implement user authentication"
- "List all in-progress tasks"
- "Complete task 123"
- "Show me task 456"
**Requirement Management:**
- "Create requirement: add dark mode support"
- "List requirements in draft status"
- "Submit requirement 789 for review"
- "Approve requirement 789"
**Daily Focus:**
- "Show today's tasks"
- "Add task 123 to today's focus"
**Documents:**
- "Show the document for task 123"
- "Update task 456's document with: [content]"
## Configuration (Optional)
The plugin uses a default API token that works for most users. If you need to use a custom token:
### Environment Variable
```bash
export AIPROJ_TOKEN="your_custom_token_here"
```
Add this to your `~/.zshrc` or `~/.bashrc` to make it persistent.
## How It Works
This plugin uses a simple, straightforward approach:
1. Makes direct REST API calls to `https://ai.pipexerp.com/api/v1`
2. Uses `curl` for HTTP requests
3. Automatically finds the API token from:
- `$AIPROJ_TOKEN` environment variable
- Built-in default token
## Advantages
-**No setup required** - works immediately after installation
-**No server process** - simple and lightweight
-**No dependencies** - just needs `curl` (built into all systems)
-**Simple architecture** - direct API calls
-**Easy debugging** - can see exact API calls
## API Endpoints
The plugin uses these endpoints:
- `GET /tasks` - List tasks
- `POST /tasks` - Create task
- `GET /tasks/{id}` - Get task details
- `PATCH /tasks/{id}` - Update task
- `DELETE /tasks/{id}` - Delete task
- `GET /requirements` - List requirements
- `POST /requirements` - Create requirement
- `POST /requirements/{id}/actions` - Update requirement status
- `GET /tasks/{id}/document` - Get task document
- `PUT /tasks/{id}/document` - Update task document
- `GET /daily-focus/tasks` - Get today's tasks
- `POST /daily-focus/tasks` - Add task to today
## Troubleshooting
### "Unauthorized" or API errors
Check your token:
```bash
echo $AIPROJ_TOKEN
```
If empty, either set the environment variable or the plugin will use the default token.
### API not responding
Verify the API is accessible:
```bash
curl -I https://ai.pipexerp.com/api/v1/health
```
### Skills not working
- Agent Skills don't create slash commands
- Just ask naturally: "create a task", "list tasks", etc.
- The plugin is automatically active when installed
## Technical Details
**API Base**: `https://ai.pipexerp.com/api/v1`
**Authentication**: Bearer token in `Authorization` header
**Data Format**: JSON
**Date Format**: ISO 8601 (YYYY-MM-DD)
**Default Project ID**: 1
## Documentation
For detailed API documentation and examples, see `skills/SKILL.md`.
## Support
- Repository: git@gitea.pipexerp.com:huangjun/claude-marketplace.git
- Author: qiudl@zhiyuncai.com

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
{
"name": "read-session-plugin",
"description": "Plugin for read-session",
"version": "1.0.0",
"author": {
"name": "qiudl"
}
}

View File

@@ -0,0 +1,211 @@
---
name: read-session
description: View a saved Claude session without reloading it for continuation
---
# Read Session Skill
You are now in session read mode. Your task is to display a previously saved conversation for review without loading it as active context for continuation.
## Instructions
1. **Get Session ID**: Extract the session ID from user's request
- Format: `YYYY-MM-DD_HHmm` (e.g., "2026-01-20_1430")
- User might provide:
- Exact ID: "2026-01-20_1430"
- Partial ID: "0120" or "1430"
- Title keywords: "read the authentication session"
- Description: "show me the JWT session from yesterday"
- If ambiguous, search `~/.claude/sessions/index.json` and ask for clarification
2. **Find Session File**:
- First check index.json to find the exact filename
- Load `~/.claude/sessions/<session-id>_<sanitized-title>.json`
- If file not found, inform user and offer similar sessions
3. **Load and Parse**: Read the JSON file and extract:
- Metadata (id, title, date, tags)
- Summary (context, approach, changes, outcome, next steps)
- Conversation history (all messages)
- File modifications
4. **Display Session**: Show a comprehensive read-only view:
```
📖 Session: [Title]
📅 Date: YYYY-MM-DD HH:mm (X days ago)
🏷️ Tags: tag1, tag2, tag3
💬 Messages: X messages
📝 Files Modified: file1, file2, file3
## Summary
**Context**: [summary.context]
**Approach**: [summary.approach]
**Key Changes**: [summary.keyChanges]
**Outcome**: [summary.outcome]
**Next Steps**: [summary.nextSteps]
## Conversation History
[Display the full conversation in a readable format]
---
To reload and continue this session: /reload-session <id>
To search for similar sessions: /search-sessions <keywords>
```
5. **Display Options**:
- View full conversation (show all messages)
- View summary only (already shown)
- View specific messages (by index or search)
- Export to different format
- Reload to continue working
## Display Modes
**Summary Mode (default):**
- Show metadata and summary only
- Don't display full conversation unless requested
**Full Mode:**
- Show metadata, summary, AND full conversation history
- Use when user says "read full session" or "show everything"
**Conversation-only Mode:**
- Skip summary, just show the conversation
- Use when user says "show me the conversation" or "messages only"
## Usage Examples
**Read summary:**
```
/read-session 2026-01-20_1430
read session 2026-01-20_1430
show me the authentication session
```
**Read full conversation:**
```
/read-session 2026-01-20_1430 --full
read full session about authentication
show me everything from the JWT session
```
**Read specific messages:**
```
/read-session 2026-01-20_1430 --messages 5-10
show messages 5-10 from session 2026-01-20_1430
```
## Important Notes
- This is a READ-ONLY view - don't load context for continuation
- Default to showing summary unless user asks for full conversation
- Format conversation messages clearly with role labels (User/Assistant)
- Show timestamps for each message if in full mode
- Provide options at the end to reload or search
- If session file not found, search index.json for similar titles
## Example Output
**Summary view (default):**
```
📖 Session: Implement User Authentication with JWT
📅 Date: Jan 18, 2026 16:00 (2 days ago)
🏷️ Tags: authentication, jwt, security, api
💬 Messages: 45 messages
📝 Files Modified: src/auth/jwt.ts, src/middleware/auth.ts, tests/auth.test.ts
## Summary
**Context**: User needed to implement JWT-based authentication for their REST API
**Approach**: Created JWT service with access/refresh tokens, added middleware for route protection
**Key Changes**:
- Implemented JWT token generation and validation
- Added authentication middleware
- Created login/logout endpoints
- Added comprehensive tests
**Outcome**: Authentication system fully implemented and tested with 95% coverage
**Next Steps**: Consider adding password reset functionality and rate limiting
---
**Actions:**
- View full conversation: /read-session 2026-01-20_1430 --full
- Reload to continue: /reload-session 2026-01-20_1430
- Search similar: /search-sessions authentication jwt
```
**Full view (with --full flag):**
```
📖 Session: Implement User Authentication with JWT
📅 Date: Jan 18, 2026 16:00 (2 days ago)
🏷️ Tags: authentication, jwt, security, api
💬 Messages: 45 messages
📝 Files Modified: src/auth/jwt.ts, src/middleware/auth.ts, tests/auth.test.ts
## Summary
[Summary content as above]
## Conversation
**[User - 16:00:00]**
I need to implement JWT authentication for my REST API
**[Assistant - 16:00:15]**
I'll help you implement JWT authentication. Let me first explore your existing codebase...
**[User - 16:02:30]**
The API is in src/api/ and uses Express
**[Assistant - 16:02:45]**
Perfect! I can see your Express setup. Let me create the JWT service...
[... full conversation continues ...]
---
**Actions:**
- Reload to continue: /reload-session 2026-01-20_1430
- Search similar: /search-sessions authentication jwt
```
**Session not found:**
```
❌ Session "2026-01-99_9999" not found.
Did you mean one of these?
1. [2026-01-20_1430] Implement User Authentication with JWT
📅 Jan 20, 14:30 | 🏷️ authentication, jwt, security
2. [2026-01-18_1600] Setup OAuth2 Integration
📅 Jan 18, 16:00 | 🏷️ oauth, authentication, api
To read: /read-session <id>
To search: /search-sessions <keywords>
```
## Formatting Guidelines
When displaying conversation messages:
- Use clear role labels: **[User]** and **[Assistant]**
- Include timestamps in full mode
- Preserve code blocks and formatting
- Show system messages if present
- Use horizontal rules to separate messages
- Limit very long messages (offer to show full text)
- Number messages for easy reference

View File

@@ -0,0 +1,8 @@
{
"name": "reload-session-plugin",
"description": "Plugin for reload-session",
"version": "1.0.0",
"author": {
"name": "qiudl"
}
}

View File

@@ -0,0 +1,177 @@
---
name: reload-session
description: Reload a previously saved Claude session to continue the conversation
---
# Reload Session Skill
You are now in session reload mode. Your task is to load a previously saved conversation and help the user continue from where they left off.
## Instructions
1. **Get Session ID**: Extract the session ID from user's request
- Format: `YYYY-MM-DD_HHmm` (e.g., "2026-01-20_1430")
- User might provide:
- Exact ID: "2026-01-20_1430"
- Partial ID: "0120" or "1430"
- Title keywords: "the authentication session"
- Description: "the one about JWT from yesterday"
- If ambiguous, search `~/.claude/sessions/index.json` and ask for clarification
2. **Find Session File**:
- First check index.json to find the exact filename
- Load `~/.claude/sessions/<session-id>_<sanitized-title>.json`
- If file not found, inform user and offer similar sessions
3. **Load and Parse**: Read the JSON file and extract:
- Metadata (id, title, date, tags)
- Summary (context, approach, changes, outcome, next steps)
- Conversation history (all messages)
- File modifications
4. **Display Session Context**: Show the user a comprehensive summary:
```
📂 Loaded Session: [Title]
📅 Original: YYYY-MM-DD HH:mm (X days ago)
🏷️ Tags: tag1, tag2, tag3
💬 Messages: X messages
📝 Modified: file1, file2, file3
## Summary
**Context**: [summary.context]
**Approach**: [summary.approach]
**Key Changes**: [summary.keyChanges]
**Outcome**: [summary.outcome]
**Next Steps**: [summary.nextSteps]
---
✅ Session loaded. I have full context of this conversation.
What would you like to work on?
```
5. **Maintain Context**:
- You now have the full conversation history available
- Reference specific details from the loaded session when helpful
- If user asks about the session, you can recall exact details
- Continue naturally from where the conversation ended
- Remember all files that were modified
- Recall all decisions and approaches discussed
6. **Options to Offer**:
- Continue the last task that was being worked on (from "Next Steps")
- Review what was accomplished
- Make changes to previous work
- Answer questions about the session
- If work continues, offer to save as a new session or update existing one
## Usage Examples
**By exact ID:**
```
/reload-session 2026-01-20_1430
```
**By partial ID:**
```
reload 0120_1430
reload 1430
```
**By title keyword:**
```
reload the dark mode session
continue the authentication work
```
**By description:**
```
continue the JWT work from yesterday
reload the session about React bugs
```
## Important Notes
- If session file not found, search index.json for similar titles and offer alternatives
- When continuing work, naturally reference the previous conversation context
- If user wants to modify previous work, ask whether to:
- Save as a new session (recommended to preserve history)
- Update the existing session (overwrite)
- Show relative dates ("2 days ago", "last week") for better context
- If files were modified in the original session, note if they still exist
- Provide actionable options based on "Next Steps" from the summary
## Example Output
**Successful load:**
```
📂 Loaded Session: Implement User Authentication with JWT
📅 Original: Jan 18, 2026 16:00 (2 days ago)
🏷️ authentication, jwt, security, api
💬 45 messages
📝 Modified: src/auth/jwt.ts, src/middleware/auth.ts, tests/auth.test.ts
## Summary
**Context**: User needed to implement JWT-based authentication for their REST API
**Approach**: Created JWT service with access/refresh tokens, added middleware for route protection
**Key Changes**:
- Implemented JWT token generation and validation
- Added authentication middleware
- Created login/logout endpoints
- Added comprehensive tests
**Outcome**: Authentication system fully implemented and tested with 95% coverage
**Next Steps**: Consider adding password reset functionality and rate limiting
---
✅ Session loaded. I have full context of this conversation.
What would you like to work on?
**Suggested options:**
1. Implement password reset functionality (from Next Steps)
2. Add rate limiting to prevent brute force attacks (from Next Steps)
3. Review or modify the existing authentication code
4. Something else
Let me know and I'll continue from where we left off!
```
**Session not found:**
```
❌ Session "2026-01-99_9999" not found.
Did you mean one of these?
1. [2026-01-20_1430] Implement User Authentication with JWT
📅 Jan 20, 14:30 | 🏷️ authentication, jwt, security
2. [2026-01-18_1600] Setup OAuth2 Integration
📅 Jan 18, 16:00 | 🏷️ oauth, authentication, api
To reload: /reload-session <id>
To search: /search-sessions <keywords>
```
**Ambiguous request:**
```
🔍 Found multiple sessions matching "authentication":
1. [2026-01-20_1430] Implement JWT Authentication
2. [2026-01-18_1600] Setup OAuth2 Integration
3. [2026-01-15_1000] Add API Authentication
Which session would you like to reload? (Use the ID)
```

View File

@@ -0,0 +1,8 @@
{
"name": "save-session-plugin",
"description": "Plugin for save-session",
"version": "1.0.0",
"author": {
"name": "qiudl"
}
}

View File

@@ -0,0 +1,206 @@
---
name: save-session
description: Auto-save Claude session conversation with AI-generated title, summary, and tags in searchable JSON format
---
# Save Session Skill
You are now in session save mode. Your task is to save the current conversation with a meaningful title and comprehensive summary.
## Auto-Save Mode
If this skill was triggered by an `<auto-save-trigger>` tag, operate in **auto-save mode**:
- Skip all user confirmations and questions
- Generate title, summary, and tags automatically
- Save immediately without asking for custom tags
- Output a brief confirmation only
## Instructions
1. **Analyze the Conversation**: Review the entire conversation history to understand:
- Main topics discussed
- Key problems solved
- Important decisions made
- Technologies or files involved
2. **Generate Title**: Create a concise, descriptive title (5-10 words) that captures the essence of the conversation.
- Use imperative or descriptive form
- Include key technologies/features mentioned
- Example: "Implement User Authentication with JWT"
- Example: "Debug Memory Leak in React Component"
3. **Generate Summary**: Write a structured summary (3-5 paragraphs) that includes:
- **Context**: What was the initial request or problem?
- **Approach**: What approach or solution was taken?
- **Key Changes**: What were the main changes or implementations?
- **Outcome**: What was achieved or what's the current status?
- **Next Steps** (if applicable): What remains to be done?
4. **Extract Tags**: Identify 3-8 relevant tags:
- Technologies used (e.g., "react", "typescript", "docker")
- Task type (e.g., "bug-fix", "feature", "refactor", "documentation")
- Domain (e.g., "authentication", "ui", "api", "database")
5. **Get Session ID**: Check `~/.claude/hooks/.current-session` for the current session ID. If it doesn't exist, generate one using format `YYYY-MM-DD_HHmm`.
6. **Filter Conversation Content**: Before saving, clean the conversation:
**EXCLUDE these from saved conversation:**
- Bash/shell command outputs (keep only the command intent)
- Full file contents from Read tool (keep only file path + brief note)
- Tool result outputs (Glob results, Grep results, etc.)
- `<system-reminder>` tags and their contents
- `<auto-save-trigger>` tags
- Raw JSON/XML tool responses
- Error stack traces (summarize the error instead)
- Large code blocks from file reads (keep only relevant snippets)
**INCLUDE and preserve:**
- User's questions and requests (the actual intent)
- Assistant's explanations and reasoning
- Key decisions and why they were made
- Code changes made (what was added/modified)
- Important findings and conclusions
- File paths that were modified
- Summary of what tools accomplished (not raw output)
**Transform tool calls to summaries:**
- Instead of: `[Bash output: 500 lines of npm install...]`
- Save as: `"Installed dependencies with npm install"`
- Instead of: `[Read file: 200 lines of code...]`
- Save as: `"Read MallPurchaseManager.java to understand order push logic"`
7. **Save to File**: Create a JSON file in `~/.claude/sessions/` with:
- Filename: `<session-id>_<sanitized-title>.json`
- Content structure:
```json
{
"id": "YYYY-MM-DD_HHmm",
"title": "[Generated Title]",
"date": "[ISO 8601 format]",
"timestamp": 1234567890,
"tags": ["tag1", "tag2"],
"summary": {
"context": "What was the initial request or problem?",
"approach": "What approach or solution was taken?",
"keyChanges": "What were the main changes or implementations?",
"outcome": "What was achieved or what's the current status?",
"nextSteps": "What remains to be done (if applicable)"
},
"metadata": {
"messageCount": 0,
"technologies": ["tech1", "tech2"],
"filesModified": ["file1", "file2"]
},
"conversation": [
{
"role": "user|assistant",
"content": "cleaned message content - NO raw tool outputs",
"timestamp": "ISO 8601",
"toolSummary": "optional: brief summary of tools used in this turn"
}
]
}
```
8. **Update Index**: After saving the session, update `~/.claude/sessions/index.json`:
- Add entry with id, title, date, tags, and summary preview
- Keep index sorted by date (newest first)
- If index doesn't exist, create it
- Index structure:
```json
{
"sessions": [
{
"id": "YYYY-MM-DD_HHmm",
"title": "Title",
"date": "ISO 8601",
"tags": ["tag1"],
"summaryPreview": "First 200 chars of context..."
}
],
"lastUpdated": "ISO 8601"
}
```
9. **Post-Save Actions**: After saving:
- Reset autosave timer: `~/.claude/hooks/autosave-mark.sh`
- Clear pending save flag: `rm -f ~/.claude/hooks/.pending-save`
10. **Confirm**: After saving, report back to the user:
- The session ID and filename
- The generated title
- Main tags extracted
- Brief confirmation with instructions on how to reload
## Important Notes
- Create `~/.claude/sessions/` directory if it doesn't exist
- Sanitize the title for filename (lowercase, replace spaces with hyphens, remove special chars)
- In manual mode, ask user if they want to add any custom tags before saving
- In auto-save mode, skip all user interactions
- Always update the index file for fast searching
- Use current timestamp in ISO 8601 format
- Count actual messages in conversation for metadata
- Each conversation MUST have its own unique JSON file
## Conversation Filtering Example
**RAW conversation (DON'T save like this):**
```
User: "找一下订单推送的代码"
Assistant: [Grep tool output with 50 matching files...]
Assistant: [Read file output with 200 lines of Java code...]
Assistant: "I found the order push code in MallPurchaseManager.java"
User: "修改它加上channelType"
Assistant: [Edit tool with full file diff...]
<system-reminder>Task tools haven't been used...</system-reminder>
```
**CLEANED conversation (Save like this):**
```json
{
"conversation": [
{
"role": "user",
"content": "找一下订单推送的代码"
},
{
"role": "assistant",
"content": "Found order push code in MallPurchaseManager.java:5536 in the getJsonString() method. This method assembles order data for pushing to Lining platform.",
"toolSummary": "Searched codebase, read MallPurchaseManager.java"
},
{
"role": "user",
"content": "修改它加上channelType"
},
{
"role": "assistant",
"content": "Added channelType field to the order push logic. Now when orders are pushed to Lining platform, it will include the external platform type (卓望/南航).",
"toolSummary": "Edited MallPurchaseManager.java - added order.setChannelType()"
}
]
}
```
## Example Output (Manual Mode)
```
Session saved successfully!
ID: 2026-01-20_1430
Title: Implement Dark Mode Toggle Feature
Tags: react, css, ui, feature
File: ~/.claude/sessions/2026-01-20_1430_implement-dark-mode-toggle-feature.json
The conversation has been archived with full history (45 messages).
To reload this session: /reload-session 2026-01-20_1430
To search sessions: /search-sessions dark mode
```
## Example Output (Auto-Save Mode)
```
[Auto-saved] 2026-01-20_1430: "Implement Dark Mode Toggle Feature" (45 messages)
```

View File

@@ -0,0 +1,8 @@
{
"name": "search-sessions-plugin",
"description": "Plugin for search-sessions",
"version": "1.0.0",
"author": {
"name": "qiudl"
}
}

View File

@@ -0,0 +1,128 @@
---
name: search-sessions
description: Search saved Claude sessions by title, tags, date, or content
---
# Search Sessions Skill
You are now in session search mode. Your task is to help the user find saved conversations.
## Instructions
1. **Read Index**: Load `~/.claude/sessions/index.json` to get the list of all sessions
- If file doesn't exist, inform user no sessions have been saved yet
2. **Parse Search Query**: Understand what the user is looking for:
- Keywords in title or summary
- Specific tags (e.g., "react", "bug-fix", "authentication")
- Date range (e.g., "last week", "January 2026", "today")
- Combination of filters
- Special flags like "--all" to show all results
3. **Search Logic**:
- First search in index.json (fast search by title, tags, date, summary preview)
- If user needs full content search, load individual session JSON files from `~/.claude/sessions/`
- Support case-insensitive search
- Support partial matches
- Score results by relevance
4. **Display Results**: Show matching sessions in a clear, readable format:
```
🔍 Found 3 sessions matching "react":
1. [2026-01-20_1430] Implement Dark Mode Toggle Feature
📅 2026-01-20 14:30 | 🏷️ react, css, ui, feature
💡 Added dark mode toggle to application settings...
2. [2026-01-19_1015] Debug Memory Leak in React Component
📅 2026-01-19 10:15 | 🏷️ react, debugging, performance
💡 Investigated and fixed memory leak caused by...
3. [2026-01-18_1600] Setup Authentication with JWT
📅 2026-01-18 16:00 | 🏷️ authentication, jwt, security
💡 Implemented JWT-based authentication system...
To reload a session: /reload-session <id>
```
5. **Advanced Search**:
- Support filtering by multiple tags: "react AND authentication"
- Support OR logic: "react OR vue"
- Support date ranges: "between 2026-01-01 and 2026-01-20"
- Support exclusion: "react NOT bug"
- Support tag-specific search: "tag:authentication"
## Search Examples
**By keyword:**
- `search sessions about react` → Search for "react" in title, tags, and summary
**By date:**
- `find sessions from last week` → Filter by date range (last 7 days)
- `sessions from January` → Filter by month
- `sessions today` → Today's sessions only
**By tag:**
- `search sessions tagged authentication` → Filter by specific tag
- `sessions with tag:bug-fix` → Explicit tag search
**List all:**
- `list all sessions` → Show all sessions (newest first)
- `show recent sessions` → Show last 10 sessions
**Combined:**
- `search react sessions from last month` → Keyword + date filter
- `find authentication AND api sessions` → Multiple tags
## Important Notes
- Show most recent sessions first by default
- Limit initial results to 10, offer to show more with --all flag
- If no matches found, suggest related searches or show recent sessions
- Provide the session ID clearly so user can reload it
- Display dates in human-readable format (e.g., "2 days ago", "last week")
- If only one result found, offer to load it automatically
## Example Output Formats
**Multiple results:**
```
🔍 Found 5 sessions matching "authentication":
1. [2026-01-20_1430] Setup OAuth2 Integration
📅 Jan 20, 14:30 (today) | 🏷️ oauth, authentication, api
💡 Integrated OAuth2 provider for third-party login...
2. [2026-01-18_1600] Implement JWT Authentication
📅 Jan 18, 16:00 (2 days ago) | 🏷️ jwt, authentication, security
💡 Built JWT-based auth system with refresh tokens...
Showing 2 of 5 results. Use "/search-sessions authentication --all" to see all.
To reload: /reload-session <id>
```
**No results:**
```
🔍 No sessions found matching "xyz"
Suggestions:
- Try different keywords
- Check spelling
- Use broader search terms
Recent sessions:
1. [2026-01-20_1430] Implement Dark Mode Toggle Feature
2. [2026-01-19_1015] Debug Memory Leak in React Component
...
```
**Single result:**
```
🔍 Found 1 session matching "oauth":
[2026-01-20_1430] Setup OAuth2 Integration
📅 Jan 20, 14:30 (today) | 🏷️ oauth, authentication, api
💡 Integrated OAuth2 provider for third-party login...
Would you like to reload this session? (/reload-session 2026-01-20_1430)
```

View File

@@ -0,0 +1,8 @@
{
"name": "session-plugin",
"description": "Manage Claude Code sessions — save, reload, read, and search saved conversations. Triggers on /session commands or when user mentions saving session, reloading session, finding previous conversations, or continuing previous work.",
"version": "1.0.0",
"author": {
"name": "qiudl"
}
}

View File

@@ -0,0 +1,149 @@
---
name: session
description: Manage Claude Code sessions — save, reload, read, and search saved conversations. Triggers on /session commands or when user mentions saving session, reloading session, finding previous conversations, or continuing previous work.
---
# Session Management Skill
Save, reload, read, and search Claude Code sessions across conversations.
## Commands
| Command | Description |
|---------|-------------|
| `/session save` | Save current conversation with auto-generated title, summary, tags |
| `/session reload <id>` | Reload a session to continue working from where you left off |
| `/session read <id>` | View a session (read-only, no context load) |
| `/session search <query>` | Search sessions by keyword, tag, or date |
---
## /session save
### Auto-Save Mode
If triggered by `<auto-save-trigger>`, skip all confirmations and save immediately.
### Steps
1. **Analyze conversation** — main topics, problems solved, decisions made, files touched
2. **Generate title** — 510 words, descriptive (e.g. "Implement JWT Authentication with Refresh Tokens")
3. **Generate summary** with sections:
- `context`: initial request or problem
- `approach`: solution taken
- `keyChanges`: main changes/implementations
- `outcome`: what was achieved
- `nextSteps`: what remains (if any)
4. **Extract tags** — 38 tags: technologies, task type, domain
5. **Get session ID** — check `~/.claude/hooks/.current-session`; if missing, use `YYYY-MM-DD_HHmm`
6. **Filter conversation before saving:**
- EXCLUDE: bash outputs, full file reads, tool results, `<system-reminder>` tags, stack traces
- INCLUDE: user requests, assistant reasoning, decisions, file paths modified, code changes summary
7. **Save to** `~/.claude/sessions/<id>_<sanitized-title>.json`:
```json
{
"id": "YYYY-MM-DD_HHmm",
"title": "...",
"date": "ISO 8601",
"timestamp": 1234567890,
"tags": ["tag1", "tag2"],
"summary": {
"context": "...", "approach": "...", "keyChanges": "...",
"outcome": "...", "nextSteps": "..."
},
"metadata": { "messageCount": 0, "technologies": [], "filesModified": [] },
"conversation": [
{ "role": "user|assistant", "content": "...", "toolSummary": "optional" }
]
}
```
8. **Update index** `~/.claude/sessions/index.json` — add entry, sort by date desc
9. **Post-save** — run `~/.claude/hooks/autosave-mark.sh`, delete `~/.claude/hooks/.pending-save`
10. **Confirm** — show session ID, filename, title, tags
### Output (manual mode)
```
Session saved!
ID: 2026-01-20_1430
Title: Implement Dark Mode Toggle Feature
Tags: react, css, ui, feature
File: ~/.claude/sessions/2026-01-20_1430_implement-dark-mode-toggle-feature.json
To reload: /session reload 2026-01-20_1430
```
### Output (auto-save)
```
[Auto-saved] 2026-01-20_1430: "Implement Dark Mode Toggle Feature" (45 messages)
```
---
## /session reload `<id>`
Load a session to **continue working** from where it left off.
### Steps
1. **Resolve ID** — accept exact ID, partial, or keywords; search index.json if ambiguous
2. **Load** `~/.claude/sessions/<id>_<title>.json`
3. **Display context:**
```
Loaded: [Title]
Date: YYYY-MM-DD HH:mm (X days ago) | Tags: ... | Messages: N
Files Modified: file1, file2
Context: ...
Approach: ...
Key Changes: ...
Outcome: ...
Next Steps: ...
Session loaded. What would you like to work on?
```
4. **Maintain context** — reference session details, remember modified files, continue naturally
5. **Offer options** based on Next Steps
---
## /session read `<id>`
View a session **without** loading it as active context.
### Steps
1. Resolve ID (same as reload)
2. Load and display — show metadata + summary by default
3. **Modes:**
- Default: metadata + summary only
- `--full`: full conversation history
- `--messages N-M`: specific message range
4. End with actions: reload link, search link
---
## /session search `<query>`
Search saved sessions.
### Steps
1. Load `~/.claude/sessions/index.json`
2. Parse query — keywords, tags (`tag:react`), dates (`last week`, `January`)
3. Search index first (fast); load individual files only for full-content search
4. Display results (newest first, limit 10):
```
Found 3 sessions matching "react":
1. [2026-01-20_1430] Implement Dark Mode Toggle Feature
Jan 20, 14:30 | react, css, ui
Added dark mode toggle to application settings...
To reload: /session reload <id>
```
5. If single result, offer to reload automatically
### Query syntax
- Keywords: `search react authentication`
- Tag filter: `tag:bug-fix`
- Date: `last week`, `today`, `January 2026`
- Boolean: `react AND jwt`, `react OR vue`, `react NOT bug`
- All: `list all sessions`

View File

@@ -0,0 +1,8 @@
{
"name": "skill-manager-plugin",
"description": "|",
"version": "1.0.0",
"author": {
"name": "qiudl"
}
}

View File

@@ -0,0 +1,306 @@
#!/bin/bash
# git-ops.sh - Git 操作封装
# 用法:
# ./git-ops.sh clone <repo> <name> - 克隆仓库
# ./git-ops.sh pull <name> - 拉取更新
# ./git-ops.sh checkout <name> <ref> - 切换版本
# ./git-ops.sh fetch <name> - 获取远程更新
# ./git-ops.sh status <name> - 查看状态
# ./git-ops.sh has-updates <name> - 检查是否有更新
set -e
SKILLS_DIR="$HOME/.claude/skills"
REPOS_DIR="$SKILLS_DIR/repos"
REGISTRY_SCRIPT="$SKILLS_DIR/skill-manager/scripts/registry.sh"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 从 URL 提取技能名称
extract_name() {
local repo="$1"
# 处理各种格式
# https://github.com/org/skill-name.git -> skill-name
# git@github.com:org/skill-name.git -> skill-name
echo "$repo" | sed -E 's|.*/([^/]+)(\.git)?$|\1|' | sed 's/\.git$//'
}
# 克隆仓库
cmd_clone() {
local repo="$1"
local name="${2:-$(extract_name "$repo")}"
if [ -z "$repo" ]; then
echo -e "${RED}用法: git-ops.sh clone <repo> [name]${NC}"
exit 1
fi
local target_dir="$REPOS_DIR/$name"
# 检查是否已存在
if [ -d "$target_dir" ]; then
echo -e "${YELLOW}技能已存在: $name${NC}"
echo "使用 'git-ops.sh pull $name' 更新"
exit 1
fi
echo -e "${BLUE}克隆技能: $name${NC}"
echo "仓库: $repo"
echo "目标: $target_dir"
echo "----------------------------------------"
# 确保 repos 目录存在
mkdir -p "$REPOS_DIR"
# 克隆仓库
if git clone "$repo" "$target_dir"; then
echo -e "${GREEN}✓ 克隆成功${NC}"
# 检查 skill.yaml
if [ ! -f "$target_dir/skill.yaml" ]; then
echo -e "${YELLOW}⚠ 警告: 未找到 skill.yaml${NC}"
fi
# 获取版本
local version="unknown"
if [ -f "$target_dir/skill.yaml" ] && command -v yq &> /dev/null; then
version=$(yq e '.version // "1.0.0"' "$target_dir/skill.yaml")
fi
# 添加到 registry
"$REGISTRY_SCRIPT" add "$name" "$repo" "$version"
echo -e "${GREEN}✓ 技能安装完成: $name ($version)${NC}"
else
echo -e "${RED}✗ 克隆失败${NC}"
exit 1
fi
}
# 拉取更新
cmd_pull() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: git-ops.sh pull <name>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
echo -e "${BLUE}更新技能: $name${NC}"
echo "----------------------------------------"
cd "$repo_dir"
# 获取当前版本
local old_version="unknown"
if [ -f "skill.yaml" ] && command -v yq &> /dev/null; then
old_version=$(yq e '.version // "unknown"' skill.yaml)
fi
# 拉取更新
if git pull origin main 2>/dev/null || git pull origin master 2>/dev/null; then
# 获取新版本
local new_version="unknown"
if [ -f "skill.yaml" ] && command -v yq &> /dev/null; then
new_version=$(yq e '.version // "unknown"' skill.yaml)
fi
if [ "$old_version" != "$new_version" ]; then
echo -e "${GREEN}✓ 版本更新: $old_version$new_version${NC}"
"$REGISTRY_SCRIPT" update "$name" version "$new_version"
else
echo -e "${GREEN}✓ 已是最新版本: $new_version${NC}"
fi
# 更新 last_updated
"$REGISTRY_SCRIPT" update "$name" last_updated "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
else
echo -e "${RED}✗ 更新失败${NC}"
exit 1
fi
}
# 切换版本
cmd_checkout() {
local name="$1"
local ref="$2"
if [ -z "$name" ] || [ -z "$ref" ]; then
echo -e "${RED}用法: git-ops.sh checkout <name> <ref>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
echo -e "${BLUE}切换版本: $name$ref${NC}"
echo "----------------------------------------"
cd "$repo_dir"
# 获取当前版本
local old_ref=$(git describe --tags --always 2>/dev/null || git rev-parse --short HEAD)
# 切换版本
if git checkout "$ref" 2>/dev/null; then
echo -e "${GREEN}✓ 版本切换: $old_ref$ref${NC}"
# 更新 registry
"$REGISTRY_SCRIPT" update "$name" version "$ref"
"$REGISTRY_SCRIPT" update "$name" last_updated "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
else
echo -e "${RED}✗ 切换失败: $ref 不存在${NC}"
echo "可用的 tag:"
git tag -l | head -10
exit 1
fi
}
# 获取远程更新
cmd_fetch() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: git-ops.sh fetch <name>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
cd "$repo_dir"
git fetch origin --tags --quiet
echo -e "${GREEN}✓ 已获取远程更新: $name${NC}"
}
# 查看状态
cmd_status() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: git-ops.sh status <name>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
echo -e "${BLUE}技能状态: $name${NC}"
echo "----------------------------------------"
cd "$repo_dir"
local branch=$(git branch --show-current 2>/dev/null || echo "detached")
local commit=$(git rev-parse --short HEAD)
local remote_url=$(git config --get remote.origin.url)
echo "分支: $branch"
echo "Commit: $commit"
echo "远程: $remote_url"
if [ -f "skill.yaml" ] && command -v yq &> /dev/null; then
local version=$(yq e '.version // "unknown"' skill.yaml)
echo "版本: $version"
fi
echo ""
echo "最近提交:"
git log --oneline -3
}
# 检查是否有更新
cmd_has_updates() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: git-ops.sh has-updates <name>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
cd "$repo_dir"
# 获取远程更新
git fetch origin --quiet 2>/dev/null || true
# 比较本地和远程
local local_head=$(git rev-parse HEAD)
local remote_head=$(git rev-parse origin/main 2>/dev/null || git rev-parse origin/master 2>/dev/null || echo "")
if [ -z "$remote_head" ]; then
echo "unknown"
exit 0
fi
if [ "$local_head" != "$remote_head" ]; then
echo "yes"
# 显示更新数量
local behind=$(git rev-list HEAD..origin/main --count 2>/dev/null || git rev-list HEAD..origin/master --count 2>/dev/null || echo "0")
echo "behind: $behind commits"
else
echo "no"
fi
}
# 主命令分发
case "${1:-help}" in
clone)
cmd_clone "$2" "$3"
;;
pull)
cmd_pull "$2"
;;
checkout)
cmd_checkout "$2" "$3"
;;
fetch)
cmd_fetch "$2"
;;
status)
cmd_status "$2"
;;
has-updates)
cmd_has_updates "$2"
;;
*)
echo "用法: git-ops.sh <command> [args]"
echo ""
echo "命令:"
echo " clone <repo> [name] 克隆仓库"
echo " pull <name> 拉取更新"
echo " checkout <name> <ref> 切换版本"
echo " fetch <name> 获取远程更新"
echo " status <name> 查看状态"
echo " has-updates <name> 检查是否有更新"
;;
esac

View File

@@ -0,0 +1,58 @@
#!/bin/bash
# init-registry.sh - 初始化技能注册表
# 用法: ./init-registry.sh
set -e
SKILLS_DIR="$HOME/.claude/skills"
REGISTRY_FILE="$SKILLS_DIR/registry.yaml"
REPOS_DIR="$SKILLS_DIR/repos"
# 颜色定义
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo "初始化 Skills Registry..."
echo "----------------------------------------"
# 创建 repos 目录
if [ ! -d "$REPOS_DIR" ]; then
mkdir -p "$REPOS_DIR"
echo -e "${GREEN}✓ 创建 repos 目录${NC}"
else
echo -e "${YELLOW}⚠ repos 目录已存在${NC}"
fi
# 创建 registry.yaml
if [ ! -f "$REGISTRY_FILE" ]; then
cat > "$REGISTRY_FILE" << 'EOF'
# Skills Registry - 技能注册表
# 由 skill-manager 自动管理,请勿手动编辑
version: 1
updated_at: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
# 全局配置
config:
auto_update_check: true # 启动时检查更新
update_check_interval: 86400 # 检查间隔默认24小时
default_branch: main # 默认分支
# 已安装技能
skills: {}
EOF
# 替换日期
sed -i '' "s/\$(date -u +\"%Y-%m-%dT%H:%M:%SZ\")/$(date -u +"%Y-%m-%dT%H:%M:%SZ")/" "$REGISTRY_FILE"
echo -e "${GREEN}✓ 创建 registry.yaml${NC}"
else
echo -e "${YELLOW}⚠ registry.yaml 已存在${NC}"
fi
echo "----------------------------------------"
echo -e "${GREEN}✓ 初始化完成${NC}"
echo ""
echo "目录结构:"
echo " $SKILLS_DIR/"
echo " ├── registry.yaml"
echo " └── repos/"

View File

@@ -0,0 +1,157 @@
# skill.yaml 规范文档
## 概述
`skill.yaml` 是技能的元数据定义文件,位于技能仓库根目录。
## 字段定义
### 必填字段
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | string | hyphen-case, ≤64字符 | 技能唯一标识 |
| `version` | string | 语义化版本 | 当前版本号 |
| `description` | string | ≤1024字符 | 技能描述 |
### 可选字段
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `author` | string | - | 作者/团队 |
| `license` | string | - | 许可证 |
| `triggers` | object | - | 触发条件 |
| `prompt_file` | string | SKILL.md | 提示词文件 |
| `dependencies` | array | [] | 依赖技能列表 |
| `mcp_servers` | array | [] | MCP 服务器依赖 |
| `hooks` | object | - | 生命周期钩子 |
| `allowed_tools` | array | - | 允许的工具 |
| `metadata` | object | - | 自定义元数据 |
## triggers 结构
```yaml
triggers:
# 关键词触发 - 用户输入包含这些词时激活
keywords:
- coolbuy-paas
- 酷采
# 命令触发 - 用户输入以这些命令开头时激活
commands:
- /skill
# 文件模式触发 - 当前目录匹配时激活
file_patterns:
- "**/coolbuy-paas/**"
```
## hooks 结构
```yaml
hooks:
# 安装后执行
post_install:
- "echo 'Installed'"
- "npm install"
# 更新后执行
post_update:
- "echo 'Updated'"
# 卸载前执行
post_uninstall:
- "echo 'Goodbye'"
```
## 完整示例
```yaml
name: coolbuy-paas
version: 1.2.0
description: |
酷采3.0 SaaS 租户端开发与测试。
支持商品管理、订单管理等模块开发。
author: your-team
license: MIT
triggers:
keywords:
- coolbuy-paas
- 酷采
- 商品管理
- 租户端
file_patterns:
- "**/coolbuy-paas/**"
prompt_file: SKILL.md
dependencies:
- dev-coding
- ops-tools
mcp_servers:
- ai-proj
- chrome-devtools
hooks:
post_install:
- "echo '✓ coolbuy-paas 技能安装完成'"
post_update:
- "echo '✓ coolbuy-paas 技能已更新'"
allowed_tools:
- Bash
- Read
- Write
- Edit
- Glob
- Grep
metadata:
category: business
tags:
- saas
- ecommerce
- tenant
min_claude_version: "1.0.0"
project_repo: "https://github.com/org/coolbuy-paas"
```
## 验证规则
1. **name**:
- 只能包含小写字母、数字、连字符
- 不能以连字符开头或结尾
- 长度 1-64 字符
2. **version**:
- 符合语义化版本规范 (SemVer)
- 格式: MAJOR.MINOR.PATCH
3. **description**:
- 长度不超过 1024 字符
- 不能包含 `<``>` 字符
4. **prompt_file**:
- 文件必须存在
- 必须是 Markdown 格式
5. **dependencies**:
- 每个依赖必须是有效的技能名称
- 不能有循环依赖
## 与 Anthropic 官方规范对比
| 字段 | 官方规范 | 本规范 | 说明 |
|------|----------|--------|------|
| name | ✓ | ✓ | 相同 |
| description | ✓ | ✓ | 相同 |
| license | ✓ | ✓ | 相同 |
| allowed-tools | ✓ | allowed_tools | 命名调整 |
| metadata | ✓ | ✓ | 相同 |
| version | ✗ | ✓ | 新增 |
| triggers | ✗ | ✓ | 新增 |
| dependencies | ✗ | ✓ | 新增 |
| hooks | ✗ | ✓ | 新增 |
| mcp_servers | ✗ | ✓ | 新增 |

View File

@@ -0,0 +1,204 @@
#!/bin/bash
# registry.sh - Registry 管理工具
# 用法:
# ./registry.sh list - 列出所有技能
# ./registry.sh get <name> - 获取技能信息
# ./registry.sh add <name> <repo> - 添加技能记录
# ./registry.sh remove <name> - 移除技能记录
# ./registry.sh update <name> <field> <value> - 更新字段
set -e
SKILLS_DIR="$HOME/.claude/skills"
REGISTRY_FILE="$SKILLS_DIR/registry.yaml"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 检查 yq 是否安装
check_yq() {
if ! command -v yq &> /dev/null; then
echo -e "${RED}错误: 需要安装 yq${NC}"
echo "安装方法: brew install yq"
exit 1
fi
}
# 检查 registry 文件
check_registry() {
if [ ! -f "$REGISTRY_FILE" ]; then
echo -e "${YELLOW}Registry 不存在,正在初始化...${NC}"
"$SKILLS_DIR/skill-manager/scripts/init-registry.sh"
fi
}
# 列出所有技能
cmd_list() {
check_yq
check_registry
echo -e "${BLUE}已安装技能:${NC}"
echo "----------------------------------------"
SKILLS=$(yq e '.skills | keys | .[]' "$REGISTRY_FILE" 2>/dev/null || echo "")
if [ -z "$SKILLS" ]; then
echo "(无已安装技能)"
return
fi
printf "%-20s %-10s %-10s %s\n" "名称" "版本" "状态" "最后更新"
echo "----------------------------------------"
for skill in $SKILLS; do
VERSION=$(yq e ".skills.$skill.version // \"unknown\"" "$REGISTRY_FILE")
STATUS=$(yq e ".skills.$skill.status // \"unknown\"" "$REGISTRY_FILE")
UPDATED=$(yq e ".skills.$skill.last_updated // \"unknown\"" "$REGISTRY_FILE" | cut -d'T' -f1)
# 状态颜色
case $STATUS in
active) STATUS_COLOR="${GREEN}$STATUS${NC}" ;;
disabled) STATUS_COLOR="${YELLOW}$STATUS${NC}" ;;
error) STATUS_COLOR="${RED}$STATUS${NC}" ;;
*) STATUS_COLOR="$STATUS" ;;
esac
printf "%-20s %-10s " "$skill" "$VERSION"
echo -e "$STATUS_COLOR\t$UPDATED"
done
}
# 获取技能信息
cmd_get() {
check_yq
check_registry
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: registry.sh get <name>${NC}"
exit 1
fi
local exists=$(yq e ".skills.$name // \"\"" "$REGISTRY_FILE")
if [ -z "$exists" ] || [ "$exists" == "null" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
echo -e "${BLUE}技能: $name${NC}"
echo "----------------------------------------"
yq e ".skills.$name" "$REGISTRY_FILE"
}
# 添加技能记录
cmd_add() {
check_yq
check_registry
local name="$1"
local repo="$2"
local version="${3:-1.0.0}"
if [ -z "$name" ] || [ -z "$repo" ]; then
echo -e "${RED}用法: registry.sh add <name> <repo> [version]${NC}"
exit 1
fi
local now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
local local_path="$SKILLS_DIR/repos/$name"
# 添加技能记录
yq e -i ".skills.$name.repo = \"$repo\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.local_path = \"$local_path\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.version = \"$version\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.installed_at = \"$now\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.last_updated = \"$now\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.status = \"active\"" "$REGISTRY_FILE"
# 更新 registry 时间
yq e -i ".updated_at = \"$now\"" "$REGISTRY_FILE"
echo -e "${GREEN}✓ 已添加技能: $name${NC}"
}
# 移除技能记录
cmd_remove() {
check_yq
check_registry
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: registry.sh remove <name>${NC}"
exit 1
fi
local exists=$(yq e ".skills.$name // \"\"" "$REGISTRY_FILE")
if [ -z "$exists" ] || [ "$exists" == "null" ]; then
echo -e "${YELLOW}技能不存在: $name${NC}"
return
fi
yq e -i "del(.skills.$name)" "$REGISTRY_FILE"
yq e -i ".updated_at = \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"" "$REGISTRY_FILE"
echo -e "${GREEN}✓ 已移除技能: $name${NC}"
}
# 更新技能字段
cmd_update() {
check_yq
check_registry
local name="$1"
local field="$2"
local value="$3"
if [ -z "$name" ] || [ -z "$field" ] || [ -z "$value" ]; then
echo -e "${RED}用法: registry.sh update <name> <field> <value>${NC}"
exit 1
fi
local exists=$(yq e ".skills.$name // \"\"" "$REGISTRY_FILE")
if [ -z "$exists" ] || [ "$exists" == "null" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
yq e -i ".skills.$name.$field = \"$value\"" "$REGISTRY_FILE"
yq e -i ".updated_at = \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"" "$REGISTRY_FILE"
echo -e "${GREEN}✓ 已更新 $name.$field = $value${NC}"
}
# 主命令分发
case "${1:-list}" in
list)
cmd_list
;;
get)
cmd_get "$2"
;;
add)
cmd_add "$2" "$3" "$4"
;;
remove)
cmd_remove "$2"
;;
update)
cmd_update "$2" "$3" "$4"
;;
*)
echo "用法: registry.sh <command> [args]"
echo ""
echo "命令:"
echo " list 列出所有技能"
echo " get <name> 获取技能信息"
echo " add <name> <repo> 添加技能记录"
echo " remove <name> 移除技能记录"
echo " update <name> <f> <v> 更新字段"
;;
esac

View File

@@ -0,0 +1,306 @@
#!/bin/bash
# git-ops.sh - Git 操作封装
# 用法:
# ./git-ops.sh clone <repo> <name> - 克隆仓库
# ./git-ops.sh pull <name> - 拉取更新
# ./git-ops.sh checkout <name> <ref> - 切换版本
# ./git-ops.sh fetch <name> - 获取远程更新
# ./git-ops.sh status <name> - 查看状态
# ./git-ops.sh has-updates <name> - 检查是否有更新
set -e
SKILLS_DIR="$HOME/.claude/skills"
REPOS_DIR="$SKILLS_DIR/repos"
REGISTRY_SCRIPT="$SKILLS_DIR/skill-manager/scripts/registry.sh"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 从 URL 提取技能名称
extract_name() {
local repo="$1"
# 处理各种格式
# https://github.com/org/skill-name.git -> skill-name
# git@github.com:org/skill-name.git -> skill-name
echo "$repo" | sed -E 's|.*/([^/]+)(\.git)?$|\1|' | sed 's/\.git$//'
}
# 克隆仓库
cmd_clone() {
local repo="$1"
local name="${2:-$(extract_name "$repo")}"
if [ -z "$repo" ]; then
echo -e "${RED}用法: git-ops.sh clone <repo> [name]${NC}"
exit 1
fi
local target_dir="$REPOS_DIR/$name"
# 检查是否已存在
if [ -d "$target_dir" ]; then
echo -e "${YELLOW}技能已存在: $name${NC}"
echo "使用 'git-ops.sh pull $name' 更新"
exit 1
fi
echo -e "${BLUE}克隆技能: $name${NC}"
echo "仓库: $repo"
echo "目标: $target_dir"
echo "----------------------------------------"
# 确保 repos 目录存在
mkdir -p "$REPOS_DIR"
# 克隆仓库
if git clone "$repo" "$target_dir"; then
echo -e "${GREEN}✓ 克隆成功${NC}"
# 检查 skill.yaml
if [ ! -f "$target_dir/skill.yaml" ]; then
echo -e "${YELLOW}⚠ 警告: 未找到 skill.yaml${NC}"
fi
# 获取版本
local version="unknown"
if [ -f "$target_dir/skill.yaml" ] && command -v yq &> /dev/null; then
version=$(yq e '.version // "1.0.0"' "$target_dir/skill.yaml")
fi
# 添加到 registry
"$REGISTRY_SCRIPT" add "$name" "$repo" "$version"
echo -e "${GREEN}✓ 技能安装完成: $name ($version)${NC}"
else
echo -e "${RED}✗ 克隆失败${NC}"
exit 1
fi
}
# 拉取更新
cmd_pull() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: git-ops.sh pull <name>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
echo -e "${BLUE}更新技能: $name${NC}"
echo "----------------------------------------"
cd "$repo_dir"
# 获取当前版本
local old_version="unknown"
if [ -f "skill.yaml" ] && command -v yq &> /dev/null; then
old_version=$(yq e '.version // "unknown"' skill.yaml)
fi
# 拉取更新
if git pull origin main 2>/dev/null || git pull origin master 2>/dev/null; then
# 获取新版本
local new_version="unknown"
if [ -f "skill.yaml" ] && command -v yq &> /dev/null; then
new_version=$(yq e '.version // "unknown"' skill.yaml)
fi
if [ "$old_version" != "$new_version" ]; then
echo -e "${GREEN}✓ 版本更新: $old_version$new_version${NC}"
"$REGISTRY_SCRIPT" update "$name" version "$new_version"
else
echo -e "${GREEN}✓ 已是最新版本: $new_version${NC}"
fi
# 更新 last_updated
"$REGISTRY_SCRIPT" update "$name" last_updated "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
else
echo -e "${RED}✗ 更新失败${NC}"
exit 1
fi
}
# 切换版本
cmd_checkout() {
local name="$1"
local ref="$2"
if [ -z "$name" ] || [ -z "$ref" ]; then
echo -e "${RED}用法: git-ops.sh checkout <name> <ref>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
echo -e "${BLUE}切换版本: $name$ref${NC}"
echo "----------------------------------------"
cd "$repo_dir"
# 获取当前版本
local old_ref=$(git describe --tags --always 2>/dev/null || git rev-parse --short HEAD)
# 切换版本
if git checkout "$ref" 2>/dev/null; then
echo -e "${GREEN}✓ 版本切换: $old_ref$ref${NC}"
# 更新 registry
"$REGISTRY_SCRIPT" update "$name" version "$ref"
"$REGISTRY_SCRIPT" update "$name" last_updated "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
else
echo -e "${RED}✗ 切换失败: $ref 不存在${NC}"
echo "可用的 tag:"
git tag -l | head -10
exit 1
fi
}
# 获取远程更新
cmd_fetch() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: git-ops.sh fetch <name>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
cd "$repo_dir"
git fetch origin --tags --quiet
echo -e "${GREEN}✓ 已获取远程更新: $name${NC}"
}
# 查看状态
cmd_status() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: git-ops.sh status <name>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
echo -e "${BLUE}技能状态: $name${NC}"
echo "----------------------------------------"
cd "$repo_dir"
local branch=$(git branch --show-current 2>/dev/null || echo "detached")
local commit=$(git rev-parse --short HEAD)
local remote_url=$(git config --get remote.origin.url)
echo "分支: $branch"
echo "Commit: $commit"
echo "远程: $remote_url"
if [ -f "skill.yaml" ] && command -v yq &> /dev/null; then
local version=$(yq e '.version // "unknown"' skill.yaml)
echo "版本: $version"
fi
echo ""
echo "最近提交:"
git log --oneline -3
}
# 检查是否有更新
cmd_has_updates() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: git-ops.sh has-updates <name>${NC}"
exit 1
fi
local repo_dir="$REPOS_DIR/$name"
if [ ! -d "$repo_dir" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
cd "$repo_dir"
# 获取远程更新
git fetch origin --quiet 2>/dev/null || true
# 比较本地和远程
local local_head=$(git rev-parse HEAD)
local remote_head=$(git rev-parse origin/main 2>/dev/null || git rev-parse origin/master 2>/dev/null || echo "")
if [ -z "$remote_head" ]; then
echo "unknown"
exit 0
fi
if [ "$local_head" != "$remote_head" ]; then
echo "yes"
# 显示更新数量
local behind=$(git rev-list HEAD..origin/main --count 2>/dev/null || git rev-list HEAD..origin/master --count 2>/dev/null || echo "0")
echo "behind: $behind commits"
else
echo "no"
fi
}
# 主命令分发
case "${1:-help}" in
clone)
cmd_clone "$2" "$3"
;;
pull)
cmd_pull "$2"
;;
checkout)
cmd_checkout "$2" "$3"
;;
fetch)
cmd_fetch "$2"
;;
status)
cmd_status "$2"
;;
has-updates)
cmd_has_updates "$2"
;;
*)
echo "用法: git-ops.sh <command> [args]"
echo ""
echo "命令:"
echo " clone <repo> [name] 克隆仓库"
echo " pull <name> 拉取更新"
echo " checkout <name> <ref> 切换版本"
echo " fetch <name> 获取远程更新"
echo " status <name> 查看状态"
echo " has-updates <name> 检查是否有更新"
;;
esac

View File

@@ -0,0 +1,58 @@
#!/bin/bash
# init-registry.sh - 初始化技能注册表
# 用法: ./init-registry.sh
set -e
SKILLS_DIR="$HOME/.claude/skills"
REGISTRY_FILE="$SKILLS_DIR/registry.yaml"
REPOS_DIR="$SKILLS_DIR/repos"
# 颜色定义
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo "初始化 Skills Registry..."
echo "----------------------------------------"
# 创建 repos 目录
if [ ! -d "$REPOS_DIR" ]; then
mkdir -p "$REPOS_DIR"
echo -e "${GREEN}✓ 创建 repos 目录${NC}"
else
echo -e "${YELLOW}⚠ repos 目录已存在${NC}"
fi
# 创建 registry.yaml
if [ ! -f "$REGISTRY_FILE" ]; then
cat > "$REGISTRY_FILE" << 'EOF'
# Skills Registry - 技能注册表
# 由 skill-manager 自动管理,请勿手动编辑
version: 1
updated_at: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
# 全局配置
config:
auto_update_check: true # 启动时检查更新
update_check_interval: 86400 # 检查间隔默认24小时
default_branch: main # 默认分支
# 已安装技能
skills: {}
EOF
# 替换日期
sed -i '' "s/\$(date -u +\"%Y-%m-%dT%H:%M:%SZ\")/$(date -u +"%Y-%m-%dT%H:%M:%SZ")/" "$REGISTRY_FILE"
echo -e "${GREEN}✓ 创建 registry.yaml${NC}"
else
echo -e "${YELLOW}⚠ registry.yaml 已存在${NC}"
fi
echo "----------------------------------------"
echo -e "${GREEN}✓ 初始化完成${NC}"
echo ""
echo "目录结构:"
echo " $SKILLS_DIR/"
echo " ├── registry.yaml"
echo " └── repos/"

View File

@@ -0,0 +1,204 @@
#!/bin/bash
# registry.sh - Registry 管理工具
# 用法:
# ./registry.sh list - 列出所有技能
# ./registry.sh get <name> - 获取技能信息
# ./registry.sh add <name> <repo> - 添加技能记录
# ./registry.sh remove <name> - 移除技能记录
# ./registry.sh update <name> <field> <value> - 更新字段
set -e
SKILLS_DIR="$HOME/.claude/skills"
REGISTRY_FILE="$SKILLS_DIR/registry.yaml"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 检查 yq 是否安装
check_yq() {
if ! command -v yq &> /dev/null; then
echo -e "${RED}错误: 需要安装 yq${NC}"
echo "安装方法: brew install yq"
exit 1
fi
}
# 检查 registry 文件
check_registry() {
if [ ! -f "$REGISTRY_FILE" ]; then
echo -e "${YELLOW}Registry 不存在,正在初始化...${NC}"
"$SKILLS_DIR/skill-manager/scripts/init-registry.sh"
fi
}
# 列出所有技能
cmd_list() {
check_yq
check_registry
echo -e "${BLUE}已安装技能:${NC}"
echo "----------------------------------------"
SKILLS=$(yq e '.skills | keys | .[]' "$REGISTRY_FILE" 2>/dev/null || echo "")
if [ -z "$SKILLS" ]; then
echo "(无已安装技能)"
return
fi
printf "%-20s %-10s %-10s %s\n" "名称" "版本" "状态" "最后更新"
echo "----------------------------------------"
for skill in $SKILLS; do
VERSION=$(yq e ".skills.$skill.version // \"unknown\"" "$REGISTRY_FILE")
STATUS=$(yq e ".skills.$skill.status // \"unknown\"" "$REGISTRY_FILE")
UPDATED=$(yq e ".skills.$skill.last_updated // \"unknown\"" "$REGISTRY_FILE" | cut -d'T' -f1)
# 状态颜色
case $STATUS in
active) STATUS_COLOR="${GREEN}$STATUS${NC}" ;;
disabled) STATUS_COLOR="${YELLOW}$STATUS${NC}" ;;
error) STATUS_COLOR="${RED}$STATUS${NC}" ;;
*) STATUS_COLOR="$STATUS" ;;
esac
printf "%-20s %-10s " "$skill" "$VERSION"
echo -e "$STATUS_COLOR\t$UPDATED"
done
}
# 获取技能信息
cmd_get() {
check_yq
check_registry
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: registry.sh get <name>${NC}"
exit 1
fi
local exists=$(yq e ".skills.$name // \"\"" "$REGISTRY_FILE")
if [ -z "$exists" ] || [ "$exists" == "null" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
echo -e "${BLUE}技能: $name${NC}"
echo "----------------------------------------"
yq e ".skills.$name" "$REGISTRY_FILE"
}
# 添加技能记录
cmd_add() {
check_yq
check_registry
local name="$1"
local repo="$2"
local version="${3:-1.0.0}"
if [ -z "$name" ] || [ -z "$repo" ]; then
echo -e "${RED}用法: registry.sh add <name> <repo> [version]${NC}"
exit 1
fi
local now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
local local_path="$SKILLS_DIR/repos/$name"
# 添加技能记录
yq e -i ".skills.$name.repo = \"$repo\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.local_path = \"$local_path\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.version = \"$version\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.installed_at = \"$now\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.last_updated = \"$now\"" "$REGISTRY_FILE"
yq e -i ".skills.$name.status = \"active\"" "$REGISTRY_FILE"
# 更新 registry 时间
yq e -i ".updated_at = \"$now\"" "$REGISTRY_FILE"
echo -e "${GREEN}✓ 已添加技能: $name${NC}"
}
# 移除技能记录
cmd_remove() {
check_yq
check_registry
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: registry.sh remove <name>${NC}"
exit 1
fi
local exists=$(yq e ".skills.$name // \"\"" "$REGISTRY_FILE")
if [ -z "$exists" ] || [ "$exists" == "null" ]; then
echo -e "${YELLOW}技能不存在: $name${NC}"
return
fi
yq e -i "del(.skills.$name)" "$REGISTRY_FILE"
yq e -i ".updated_at = \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"" "$REGISTRY_FILE"
echo -e "${GREEN}✓ 已移除技能: $name${NC}"
}
# 更新技能字段
cmd_update() {
check_yq
check_registry
local name="$1"
local field="$2"
local value="$3"
if [ -z "$name" ] || [ -z "$field" ] || [ -z "$value" ]; then
echo -e "${RED}用法: registry.sh update <name> <field> <value>${NC}"
exit 1
fi
local exists=$(yq e ".skills.$name // \"\"" "$REGISTRY_FILE")
if [ -z "$exists" ] || [ "$exists" == "null" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
yq e -i ".skills.$name.$field = \"$value\"" "$REGISTRY_FILE"
yq e -i ".updated_at = \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"" "$REGISTRY_FILE"
echo -e "${GREEN}✓ 已更新 $name.$field = $value${NC}"
}
# 主命令分发
case "${1:-list}" in
list)
cmd_list
;;
get)
cmd_get "$2"
;;
add)
cmd_add "$2" "$3" "$4"
;;
remove)
cmd_remove "$2"
;;
update)
cmd_update "$2" "$3" "$4"
;;
*)
echo "用法: registry.sh <command> [args]"
echo ""
echo "命令:"
echo " list 列出所有技能"
echo " get <name> 获取技能信息"
echo " add <name> <repo> 添加技能记录"
echo " remove <name> 移除技能记录"
echo " update <name> <f> <v> 更新字段"
;;
esac

View File

@@ -0,0 +1,393 @@
#!/bin/bash
# skill.sh - Skills 管理器主入口
# 用法:
# ./skill.sh list - 列出已安装技能
# ./skill.sh install <repo> - 安装新技能
# ./skill.sh upgrade [name] - 升级技能
# ./skill.sh uninstall <name> - 卸载技能
# ./skill.sh rollback <name> <version> - 回滚版本
# ./skill.sh info <name> - 查看技能详情
# ./skill.sh enable <name> - 启用技能
# ./skill.sh disable <name> - 禁用技能
# ./skill.sh check - 检查所有技能更新
set -e
SKILLS_DIR="$HOME/.claude/skills"
REPOS_DIR="$SKILLS_DIR/repos"
SCRIPTS_DIR="$SKILLS_DIR/skill-manager/scripts"
REGISTRY_SCRIPT="$SCRIPTS_DIR/registry.sh"
GIT_OPS_SCRIPT="$SCRIPTS_DIR/git-ops.sh"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 显示 banner
show_banner() {
echo -e "${CYAN}"
echo "╔═══════════════════════════════════════╗"
echo "║ Skills Manager v1.0.0 ║"
echo "║ 自我进化技能管理器 ║"
echo "╚═══════════════════════════════════════╝"
echo -e "${NC}"
}
# 从 URL 提取技能名称
extract_name() {
local repo="$1"
echo "$repo" | sed -E 's|.*/([^/]+)(\.git)?$|\1|' | sed 's/\.git$//'
}
# 列出所有技能
cmd_list() {
echo -e "${BLUE}已安装技能:${NC}"
echo "════════════════════════════════════════"
"$REGISTRY_SCRIPT" list
}
# 安装技能
cmd_install() {
local repo="$1"
if [ -z "$repo" ]; then
echo -e "${RED}用法: skill install <repo>${NC}"
echo "示例: skill install https://github.com/org/skill-name.git"
exit 1
fi
local name=$(extract_name "$repo")
echo -e "${BLUE}安装技能: $name${NC}"
echo "════════════════════════════════════════"
# 检查是否已安装
if [ -d "$REPOS_DIR/$name" ]; then
echo -e "${YELLOW}技能已安装,使用 'skill upgrade $name' 更新${NC}"
exit 1
fi
# 克隆仓库
"$GIT_OPS_SCRIPT" clone "$repo" "$name"
# 验证 skill.yaml
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
"$SCRIPTS_DIR/validate.sh" "$REPOS_DIR/$name"
fi
# 创建符号链接到 skills 目录
local skill_dir="$SKILLS_DIR/$name"
if [ ! -d "$skill_dir" ]; then
mkdir -p "$skill_dir"
# 复制 SKILL.md
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
cp "$REPOS_DIR/$name/$prompt_file" "$skill_dir/SKILL.md"
fi
fi
# 执行 post_install 钩子
if command -v yq &> /dev/null && [ -f "$skill_yaml" ]; then
local hooks=$(yq e '.hooks.post_install[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行安装后钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
echo ""
echo -e "${GREEN}✓ 技能安装完成: $name${NC}"
}
# 升级技能
cmd_upgrade() {
local name="$1"
echo -e "${BLUE}升级技能${NC}"
echo "════════════════════════════════════════"
if [ -z "$name" ]; then
# 升级所有技能
echo "升级所有技能..."
local skills=$(ls -1 "$REPOS_DIR" 2>/dev/null || echo "")
if [ -z "$skills" ]; then
echo "没有已安装的技能"
exit 0
fi
for skill in $skills; do
echo ""
echo -e "${CYAN}>>> $skill${NC}"
"$GIT_OPS_SCRIPT" pull "$skill" || echo -e "${YELLOW}跳过 $skill${NC}"
done
else
# 升级指定技能
if [ ! -d "$REPOS_DIR/$name" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
"$GIT_OPS_SCRIPT" pull "$name"
# 同步 SKILL.md
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
mkdir -p "$SKILLS_DIR/$name"
cp "$REPOS_DIR/$name/$prompt_file" "$SKILLS_DIR/$name/SKILL.md"
fi
# 执行 post_update 钩子
local hooks=$(yq e '.hooks.post_update[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行更新后钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
fi
echo ""
echo -e "${GREEN}✓ 升级完成${NC}"
}
# 卸载技能
cmd_uninstall() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill uninstall <name>${NC}"
exit 1
fi
echo -e "${BLUE}卸载技能: $name${NC}"
echo "════════════════════════════════════════"
if [ ! -d "$REPOS_DIR/$name" ]; then
echo -e "${YELLOW}技能不存在: $name${NC}"
exit 0
fi
# 确认
echo -e "${YELLOW}确认卸载 $name? [y/N]${NC}"
read -r confirm
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
echo "取消卸载"
exit 0
fi
# 执行 post_uninstall 钩子
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if command -v yq &> /dev/null && [ -f "$skill_yaml" ]; then
local hooks=$(yq e '.hooks.post_uninstall[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行卸载钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
# 删除仓库
rm -rf "$REPOS_DIR/$name"
echo -e "${GREEN}✓ 已删除仓库: $REPOS_DIR/$name${NC}"
# 删除技能目录
rm -rf "$SKILLS_DIR/$name"
echo -e "${GREEN}✓ 已删除技能目录: $SKILLS_DIR/$name${NC}"
# 从 registry 移除
"$REGISTRY_SCRIPT" remove "$name"
echo ""
echo -e "${GREEN}✓ 技能已卸载: $name${NC}"
}
# 回滚版本
cmd_rollback() {
local name="$1"
local version="$2"
if [ -z "$name" ] || [ -z "$version" ]; then
echo -e "${RED}用法: skill rollback <name> <version>${NC}"
exit 1
fi
echo -e "${BLUE}回滚技能: $name$version${NC}"
echo "════════════════════════════════════════"
"$GIT_OPS_SCRIPT" checkout "$name" "$version"
# 同步 SKILL.md
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
mkdir -p "$SKILLS_DIR/$name"
cp "$REPOS_DIR/$name/$prompt_file" "$SKILLS_DIR/$name/SKILL.md"
fi
fi
echo ""
echo -e "${GREEN}✓ 回滚完成${NC}"
}
# 查看技能详情
cmd_info() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill info <name>${NC}"
exit 1
fi
echo -e "${BLUE}技能详情: $name${NC}"
echo "════════════════════════════════════════"
"$GIT_OPS_SCRIPT" status "$name"
# 显示 skill.yaml 信息
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ] && command -v yq &> /dev/null; then
echo ""
echo "技能配置:"
echo "----------------------------------------"
echo "描述: $(yq e '.description' "$skill_yaml" | head -1)"
echo "作者: $(yq e '.author // "未知"' "$skill_yaml")"
local deps=$(yq e '.dependencies[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$deps" ]; then
echo "依赖: $deps"
fi
local keywords=$(yq e '.triggers.keywords[]' "$skill_yaml" 2>/dev/null | tr '\n' ', ' | sed 's/,$//')
if [ -n "$keywords" ]; then
echo "关键词: $keywords"
fi
fi
}
# 启用技能
cmd_enable() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill enable <name>${NC}"
exit 1
fi
"$REGISTRY_SCRIPT" update "$name" status "active"
echo -e "${GREEN}✓ 已启用技能: $name${NC}"
}
# 禁用技能
cmd_disable() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill disable <name>${NC}"
exit 1
fi
"$REGISTRY_SCRIPT" update "$name" status "disabled"
echo -e "${GREEN}✓ 已禁用技能: $name${NC}"
}
# 检查更新
cmd_check() {
echo -e "${BLUE}检查技能更新${NC}"
echo "════════════════════════════════════════"
local skills=$(ls -1 "$REPOS_DIR" 2>/dev/null || echo "")
if [ -z "$skills" ]; then
echo "没有已安装的技能"
exit 0
fi
local has_updates=0
for skill in $skills; do
local result=$("$GIT_OPS_SCRIPT" has-updates "$skill" 2>/dev/null | head -1)
if [ "$result" == "yes" ]; then
local behind=$("$GIT_OPS_SCRIPT" has-updates "$skill" 2>/dev/null | tail -1)
echo -e "${YELLOW}$skill 有更新 ($behind)${NC}"
has_updates=1
else
echo -e "${GREEN}$skill 已是最新${NC}"
fi
done
echo ""
if [ $has_updates -eq 1 ]; then
echo -e "${YELLOW}执行 'skill upgrade' 升级所有技能${NC}"
else
echo -e "${GREEN}所有技能已是最新版本${NC}"
fi
}
# 主命令分发
case "${1:-help}" in
list|ls)
cmd_list
;;
install|add)
cmd_install "$2"
;;
upgrade|update)
cmd_upgrade "$2"
;;
uninstall|remove)
cmd_uninstall "$2"
;;
rollback)
cmd_rollback "$2" "$3"
;;
info|show)
cmd_info "$2"
;;
enable)
cmd_enable "$2"
;;
disable)
cmd_disable "$2"
;;
check)
cmd_check
;;
help|--help|-h)
show_banner
echo "用法: skill <command> [args]"
echo ""
echo "命令:"
echo " list 列出已安装技能"
echo " install <repo> 安装新技能"
echo " upgrade [name] 升级技能(不指定则升级全部)"
echo " uninstall <name> 卸载技能"
echo " rollback <name> <version> 回滚到指定版本"
echo " info <name> 查看技能详情"
echo " enable <name> 启用技能"
echo " disable <name> 禁用技能"
echo " check 检查所有技能更新"
echo ""
echo "示例:"
echo " skill install https://github.com/org/skill-name.git"
echo " skill upgrade coolbuy-paas"
echo " skill rollback coolbuy-paas v1.1.0"
;;
*)
echo -e "${RED}未知命令: $1${NC}"
echo "使用 'skill help' 查看帮助"
exit 1
;;
esac

View File

@@ -0,0 +1,88 @@
#!/bin/bash
# validate.sh - 验证 skill.yaml 格式
# 用法: ./validate.sh <skill-directory>
set -e
SKILL_DIR="${1:-.}"
SKILL_YAML="$SKILL_DIR/skill.yaml"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo "验证技能目录: $SKILL_DIR"
echo "----------------------------------------"
# 检查 skill.yaml 是否存在
if [ ! -f "$SKILL_YAML" ]; then
echo -e "${RED}✗ skill.yaml 不存在${NC}"
exit 1
fi
echo -e "${GREEN}✓ skill.yaml 存在${NC}"
# 检查 YAML 格式(需要 yq 或 python
if command -v yq &> /dev/null; then
if ! yq e '.' "$SKILL_YAML" > /dev/null 2>&1; then
echo -e "${RED}✗ YAML 格式错误${NC}"
exit 1
fi
echo -e "${GREEN}✓ YAML 格式正确${NC}"
# 检查必填字段
NAME=$(yq e '.name' "$SKILL_YAML")
VERSION=$(yq e '.version' "$SKILL_YAML")
DESCRIPTION=$(yq e '.description' "$SKILL_YAML")
if [ "$NAME" == "null" ] || [ -z "$NAME" ]; then
echo -e "${RED}✗ 缺少必填字段: name${NC}"
exit 1
fi
echo -e "${GREEN}✓ name: $NAME${NC}"
if [ "$VERSION" == "null" ] || [ -z "$VERSION" ]; then
echo -e "${RED}✗ 缺少必填字段: version${NC}"
exit 1
fi
echo -e "${GREEN}✓ version: $VERSION${NC}"
if [ "$DESCRIPTION" == "null" ] || [ -z "$DESCRIPTION" ]; then
echo -e "${RED}✗ 缺少必填字段: description${NC}"
exit 1
fi
echo -e "${GREEN}✓ description: 已设置${NC}"
# 验证 name 格式
if ! echo "$NAME" | grep -qE '^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$'; then
echo -e "${RED}✗ name 格式错误: 只能包含小写字母、数字、连字符${NC}"
exit 1
fi
if [ ${#NAME} -gt 64 ]; then
echo -e "${RED}✗ name 超过 64 字符${NC}"
exit 1
fi
echo -e "${GREEN}✓ name 格式正确${NC}"
# 检查 prompt_file
PROMPT_FILE=$(yq e '.prompt_file // "SKILL.md"' "$SKILL_YAML")
if [ ! -f "$SKILL_DIR/$PROMPT_FILE" ]; then
echo -e "${YELLOW}⚠ prompt_file 不存在: $PROMPT_FILE${NC}"
else
echo -e "${GREEN}✓ prompt_file: $PROMPT_FILE${NC}"
fi
elif command -v python3 &> /dev/null; then
python3 -c "import yaml; yaml.safe_load(open('$SKILL_YAML'))" 2>/dev/null
if [ $? -ne 0 ]; then
echo -e "${RED}✗ YAML 格式错误${NC}"
exit 1
fi
echo -e "${GREEN}✓ YAML 格式正确 (使用 Python 验证)${NC}"
else
echo -e "${YELLOW}⚠ 无法验证 YAML 格式 (需要 yq 或 python3)${NC}"
fi
echo "----------------------------------------"
echo -e "${GREEN}✓ 验证通过${NC}"

View File

@@ -0,0 +1,157 @@
# skill.yaml 规范文档
## 概述
`skill.yaml` 是技能的元数据定义文件,位于技能仓库根目录。
## 字段定义
### 必填字段
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | string | hyphen-case, ≤64字符 | 技能唯一标识 |
| `version` | string | 语义化版本 | 当前版本号 |
| `description` | string | ≤1024字符 | 技能描述 |
### 可选字段
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `author` | string | - | 作者/团队 |
| `license` | string | - | 许可证 |
| `triggers` | object | - | 触发条件 |
| `prompt_file` | string | SKILL.md | 提示词文件 |
| `dependencies` | array | [] | 依赖技能列表 |
| `mcp_servers` | array | [] | MCP 服务器依赖 |
| `hooks` | object | - | 生命周期钩子 |
| `allowed_tools` | array | - | 允许的工具 |
| `metadata` | object | - | 自定义元数据 |
## triggers 结构
```yaml
triggers:
# 关键词触发 - 用户输入包含这些词时激活
keywords:
- coolbuy-paas
- 酷采
# 命令触发 - 用户输入以这些命令开头时激活
commands:
- /skill
# 文件模式触发 - 当前目录匹配时激活
file_patterns:
- "**/coolbuy-paas/**"
```
## hooks 结构
```yaml
hooks:
# 安装后执行
post_install:
- "echo 'Installed'"
- "npm install"
# 更新后执行
post_update:
- "echo 'Updated'"
# 卸载前执行
post_uninstall:
- "echo 'Goodbye'"
```
## 完整示例
```yaml
name: coolbuy-paas
version: 1.2.0
description: |
酷采3.0 SaaS 租户端开发与测试。
支持商品管理、订单管理等模块开发。
author: your-team
license: MIT
triggers:
keywords:
- coolbuy-paas
- 酷采
- 商品管理
- 租户端
file_patterns:
- "**/coolbuy-paas/**"
prompt_file: SKILL.md
dependencies:
- dev-coding
- ops-tools
mcp_servers:
- ai-proj
- chrome-devtools
hooks:
post_install:
- "echo '✓ coolbuy-paas 技能安装完成'"
post_update:
- "echo '✓ coolbuy-paas 技能已更新'"
allowed_tools:
- Bash
- Read
- Write
- Edit
- Glob
- Grep
metadata:
category: business
tags:
- saas
- ecommerce
- tenant
min_claude_version: "1.0.0"
project_repo: "https://github.com/org/coolbuy-paas"
```
## 验证规则
1. **name**:
- 只能包含小写字母、数字、连字符
- 不能以连字符开头或结尾
- 长度 1-64 字符
2. **version**:
- 符合语义化版本规范 (SemVer)
- 格式: MAJOR.MINOR.PATCH
3. **description**:
- 长度不超过 1024 字符
- 不能包含 `<``>` 字符
4. **prompt_file**:
- 文件必须存在
- 必须是 Markdown 格式
5. **dependencies**:
- 每个依赖必须是有效的技能名称
- 不能有循环依赖
## 与 Anthropic 官方规范对比
| 字段 | 官方规范 | 本规范 | 说明 |
|------|----------|--------|------|
| name | ✓ | ✓ | 相同 |
| description | ✓ | ✓ | 相同 |
| license | ✓ | ✓ | 相同 |
| allowed-tools | ✓ | allowed_tools | 命名调整 |
| metadata | ✓ | ✓ | 相同 |
| version | ✗ | ✓ | 新增 |
| triggers | ✗ | ✓ | 新增 |
| dependencies | ✗ | ✓ | 新增 |
| hooks | ✗ | ✓ | 新增 |
| mcp_servers | ✗ | ✓ | 新增 |

View File

@@ -0,0 +1,393 @@
#!/bin/bash
# skill.sh - Skills 管理器主入口
# 用法:
# ./skill.sh list - 列出已安装技能
# ./skill.sh install <repo> - 安装新技能
# ./skill.sh upgrade [name] - 升级技能
# ./skill.sh uninstall <name> - 卸载技能
# ./skill.sh rollback <name> <version> - 回滚版本
# ./skill.sh info <name> - 查看技能详情
# ./skill.sh enable <name> - 启用技能
# ./skill.sh disable <name> - 禁用技能
# ./skill.sh check - 检查所有技能更新
set -e
SKILLS_DIR="$HOME/.claude/skills"
REPOS_DIR="$SKILLS_DIR/repos"
SCRIPTS_DIR="$SKILLS_DIR/skill-manager/scripts"
REGISTRY_SCRIPT="$SCRIPTS_DIR/registry.sh"
GIT_OPS_SCRIPT="$SCRIPTS_DIR/git-ops.sh"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 显示 banner
show_banner() {
echo -e "${CYAN}"
echo "╔═══════════════════════════════════════╗"
echo "║ Skills Manager v1.0.0 ║"
echo "║ 自我进化技能管理器 ║"
echo "╚═══════════════════════════════════════╝"
echo -e "${NC}"
}
# 从 URL 提取技能名称
extract_name() {
local repo="$1"
echo "$repo" | sed -E 's|.*/([^/]+)(\.git)?$|\1|' | sed 's/\.git$//'
}
# 列出所有技能
cmd_list() {
echo -e "${BLUE}已安装技能:${NC}"
echo "════════════════════════════════════════"
"$REGISTRY_SCRIPT" list
}
# 安装技能
cmd_install() {
local repo="$1"
if [ -z "$repo" ]; then
echo -e "${RED}用法: skill install <repo>${NC}"
echo "示例: skill install https://github.com/org/skill-name.git"
exit 1
fi
local name=$(extract_name "$repo")
echo -e "${BLUE}安装技能: $name${NC}"
echo "════════════════════════════════════════"
# 检查是否已安装
if [ -d "$REPOS_DIR/$name" ]; then
echo -e "${YELLOW}技能已安装,使用 'skill upgrade $name' 更新${NC}"
exit 1
fi
# 克隆仓库
"$GIT_OPS_SCRIPT" clone "$repo" "$name"
# 验证 skill.yaml
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
"$SCRIPTS_DIR/validate.sh" "$REPOS_DIR/$name"
fi
# 创建符号链接到 skills 目录
local skill_dir="$SKILLS_DIR/$name"
if [ ! -d "$skill_dir" ]; then
mkdir -p "$skill_dir"
# 复制 SKILL.md
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
cp "$REPOS_DIR/$name/$prompt_file" "$skill_dir/SKILL.md"
fi
fi
# 执行 post_install 钩子
if command -v yq &> /dev/null && [ -f "$skill_yaml" ]; then
local hooks=$(yq e '.hooks.post_install[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行安装后钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
echo ""
echo -e "${GREEN}✓ 技能安装完成: $name${NC}"
}
# 升级技能
cmd_upgrade() {
local name="$1"
echo -e "${BLUE}升级技能${NC}"
echo "════════════════════════════════════════"
if [ -z "$name" ]; then
# 升级所有技能
echo "升级所有技能..."
local skills=$(ls -1 "$REPOS_DIR" 2>/dev/null || echo "")
if [ -z "$skills" ]; then
echo "没有已安装的技能"
exit 0
fi
for skill in $skills; do
echo ""
echo -e "${CYAN}>>> $skill${NC}"
"$GIT_OPS_SCRIPT" pull "$skill" || echo -e "${YELLOW}跳过 $skill${NC}"
done
else
# 升级指定技能
if [ ! -d "$REPOS_DIR/$name" ]; then
echo -e "${RED}技能不存在: $name${NC}"
exit 1
fi
"$GIT_OPS_SCRIPT" pull "$name"
# 同步 SKILL.md
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
mkdir -p "$SKILLS_DIR/$name"
cp "$REPOS_DIR/$name/$prompt_file" "$SKILLS_DIR/$name/SKILL.md"
fi
# 执行 post_update 钩子
local hooks=$(yq e '.hooks.post_update[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行更新后钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
fi
echo ""
echo -e "${GREEN}✓ 升级完成${NC}"
}
# 卸载技能
cmd_uninstall() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill uninstall <name>${NC}"
exit 1
fi
echo -e "${BLUE}卸载技能: $name${NC}"
echo "════════════════════════════════════════"
if [ ! -d "$REPOS_DIR/$name" ]; then
echo -e "${YELLOW}技能不存在: $name${NC}"
exit 0
fi
# 确认
echo -e "${YELLOW}确认卸载 $name? [y/N]${NC}"
read -r confirm
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
echo "取消卸载"
exit 0
fi
# 执行 post_uninstall 钩子
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if command -v yq &> /dev/null && [ -f "$skill_yaml" ]; then
local hooks=$(yq e '.hooks.post_uninstall[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$hooks" ]; then
echo -e "${YELLOW}执行卸载钩子...${NC}"
echo "$hooks" | while read -r cmd; do
[ -n "$cmd" ] && eval "$cmd"
done
fi
fi
# 删除仓库
rm -rf "$REPOS_DIR/$name"
echo -e "${GREEN}✓ 已删除仓库: $REPOS_DIR/$name${NC}"
# 删除技能目录
rm -rf "$SKILLS_DIR/$name"
echo -e "${GREEN}✓ 已删除技能目录: $SKILLS_DIR/$name${NC}"
# 从 registry 移除
"$REGISTRY_SCRIPT" remove "$name"
echo ""
echo -e "${GREEN}✓ 技能已卸载: $name${NC}"
}
# 回滚版本
cmd_rollback() {
local name="$1"
local version="$2"
if [ -z "$name" ] || [ -z "$version" ]; then
echo -e "${RED}用法: skill rollback <name> <version>${NC}"
exit 1
fi
echo -e "${BLUE}回滚技能: $name$version${NC}"
echo "════════════════════════════════════════"
"$GIT_OPS_SCRIPT" checkout "$name" "$version"
# 同步 SKILL.md
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ]; then
local prompt_file=$(yq e '.prompt_file // "SKILL.md"' "$skill_yaml" 2>/dev/null || echo "SKILL.md")
if [ -f "$REPOS_DIR/$name/$prompt_file" ]; then
mkdir -p "$SKILLS_DIR/$name"
cp "$REPOS_DIR/$name/$prompt_file" "$SKILLS_DIR/$name/SKILL.md"
fi
fi
echo ""
echo -e "${GREEN}✓ 回滚完成${NC}"
}
# 查看技能详情
cmd_info() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill info <name>${NC}"
exit 1
fi
echo -e "${BLUE}技能详情: $name${NC}"
echo "════════════════════════════════════════"
"$GIT_OPS_SCRIPT" status "$name"
# 显示 skill.yaml 信息
local skill_yaml="$REPOS_DIR/$name/skill.yaml"
if [ -f "$skill_yaml" ] && command -v yq &> /dev/null; then
echo ""
echo "技能配置:"
echo "----------------------------------------"
echo "描述: $(yq e '.description' "$skill_yaml" | head -1)"
echo "作者: $(yq e '.author // "未知"' "$skill_yaml")"
local deps=$(yq e '.dependencies[]' "$skill_yaml" 2>/dev/null || echo "")
if [ -n "$deps" ]; then
echo "依赖: $deps"
fi
local keywords=$(yq e '.triggers.keywords[]' "$skill_yaml" 2>/dev/null | tr '\n' ', ' | sed 's/,$//')
if [ -n "$keywords" ]; then
echo "关键词: $keywords"
fi
fi
}
# 启用技能
cmd_enable() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill enable <name>${NC}"
exit 1
fi
"$REGISTRY_SCRIPT" update "$name" status "active"
echo -e "${GREEN}✓ 已启用技能: $name${NC}"
}
# 禁用技能
cmd_disable() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}用法: skill disable <name>${NC}"
exit 1
fi
"$REGISTRY_SCRIPT" update "$name" status "disabled"
echo -e "${GREEN}✓ 已禁用技能: $name${NC}"
}
# 检查更新
cmd_check() {
echo -e "${BLUE}检查技能更新${NC}"
echo "════════════════════════════════════════"
local skills=$(ls -1 "$REPOS_DIR" 2>/dev/null || echo "")
if [ -z "$skills" ]; then
echo "没有已安装的技能"
exit 0
fi
local has_updates=0
for skill in $skills; do
local result=$("$GIT_OPS_SCRIPT" has-updates "$skill" 2>/dev/null | head -1)
if [ "$result" == "yes" ]; then
local behind=$("$GIT_OPS_SCRIPT" has-updates "$skill" 2>/dev/null | tail -1)
echo -e "${YELLOW}$skill 有更新 ($behind)${NC}"
has_updates=1
else
echo -e "${GREEN}$skill 已是最新${NC}"
fi
done
echo ""
if [ $has_updates -eq 1 ]; then
echo -e "${YELLOW}执行 'skill upgrade' 升级所有技能${NC}"
else
echo -e "${GREEN}所有技能已是最新版本${NC}"
fi
}
# 主命令分发
case "${1:-help}" in
list|ls)
cmd_list
;;
install|add)
cmd_install "$2"
;;
upgrade|update)
cmd_upgrade "$2"
;;
uninstall|remove)
cmd_uninstall "$2"
;;
rollback)
cmd_rollback "$2" "$3"
;;
info|show)
cmd_info "$2"
;;
enable)
cmd_enable "$2"
;;
disable)
cmd_disable "$2"
;;
check)
cmd_check
;;
help|--help|-h)
show_banner
echo "用法: skill <command> [args]"
echo ""
echo "命令:"
echo " list 列出已安装技能"
echo " install <repo> 安装新技能"
echo " upgrade [name] 升级技能(不指定则升级全部)"
echo " uninstall <name> 卸载技能"
echo " rollback <name> <version> 回滚到指定版本"
echo " info <name> 查看技能详情"
echo " enable <name> 启用技能"
echo " disable <name> 禁用技能"
echo " check 检查所有技能更新"
echo ""
echo "示例:"
echo " skill install https://github.com/org/skill-name.git"
echo " skill upgrade coolbuy-paas"
echo " skill rollback coolbuy-paas v1.1.0"
;;
*)
echo -e "${RED}未知命令: $1${NC}"
echo "使用 'skill help' 查看帮助"
exit 1
;;
esac

View File

@@ -0,0 +1,315 @@
---
name: skill-manager
description: 自我进化 Skills 管理器。通过自然语言安装、升级、卸载、回滚技能。
---
# Skill Manager - 技能管理器
基于 Git 的 Claude Code 技能管理系统,支持自然语言操作。
---
## 命令列表
| 命令 | 功能 | 示例 |
|------|------|------|
| `/skill list` | 列出已安装技能 | `/skill list` |
| `/skill install <repo>` | 安装新技能 | `/skill install https://github.com/org/skill-name` |
| `/skill upgrade [name]` | 升级技能 | `/skill upgrade coolbuy-paas` |
| `/skill uninstall <name>` | 卸载技能 | `/skill uninstall dev-test` |
| `/skill rollback <name> <ver>` | 回滚版本 | `/skill rollback coolbuy-paas v1.1.0` |
| `/skill info <name>` | 查看技能详情 | `/skill info coolbuy-paas` |
| `/skill enable <name>` | 启用技能 | `/skill enable ops-tools` |
| `/skill disable <name>` | 禁用技能 | `/skill disable ops-tools` |
| `/skill check` | 检查更新 | `/skill check` |
---
## 自然语言支持
支持中英文自然语言指令:
| 自然语言 | 解析为 |
|----------|--------|
| "升级 coolbuy-paas" | `/skill upgrade coolbuy-paas` |
| "更新所有技能" | `/skill upgrade` |
| "安装技能 https://..." | `/skill install https://...` |
| "卸载 dev-test 技能" | `/skill uninstall dev-test` |
| "列出所有技能" | `/skill list` |
| "回滚到 v1.1.0" | `/skill rollback <name> v1.1.0` |
| "xxx 是什么版本" | `/skill info xxx` |
---
## 命令处理指南
### `/skill list`
**列出所有已安装技能**
执行步骤:
1. 读取 `~/.claude/skills/registry.yaml`
2. 遍历所有技能目录
3. 读取每个技能的 `skill.yaml` 获取版本信息
4. 按名称排序显示
输出格式:
```
已安装技能 (N个):
| 名称 | 版本 | 状态 | 最后更新 |
|------|------|------|----------|
| skill-name | v1.0.0 | active | 2026-01-20 |
```
---
### `/skill install <repo>`
**从 Git 仓库安装新技能**
执行步骤:
1. 验证仓库地址格式HTTPS 或 SSH
2. 从 URL 提取技能名称
3. 检查是否已安装(如已安装,提示使用 upgrade
4. 执行 `git clone``~/.claude/skills/repos/<name>/`
5. 验证 `skill.yaml` 存在且格式正确
6. 检查并安装依赖技能
7. 更新 `registry.yaml`
8. 创建符号链接或复制到 `~/.claude/skills/<name>/`
9. 执行 `post_install` 钩子
10. 显示安装结果
示例:
```bash
# HTTPS
/skill install https://github.com/org/skill-coolbuy-paas.git
# SSH
/skill install git@github.com:org/skill-coolbuy-paas.git
```
---
### `/skill upgrade [name]`
**升级指定技能或全部技能**
执行步骤:
1. 如果指定 name升级单个技能否则升级全部
2. 读取 `registry.yaml` 获取仓库路径
3. 进入仓库目录执行 `git fetch origin`
4. 比较本地和远程版本
5. 如有更新:
- 执行 `git pull origin main`
- 读取新版本号
- 更新 `registry.yaml`
- 执行 `post_update` 钩子
- 同步文件到 `~/.claude/skills/<name>/`
6. 显示更新结果
---
### `/skill uninstall <name>`
**卸载已安装的技能**
执行步骤:
1. 验证技能存在
2. 检查是否有其他技能依赖此技能
3. 如有依赖,显示警告并询问确认
4. 执行 `post_uninstall` 钩子
5. 删除仓库目录 `~/.claude/skills/repos/<name>/`
6. 删除技能目录 `~/.claude/skills/<name>/`
7.`registry.yaml` 移除记录
8. 显示卸载结果
---
### `/skill rollback <name> <version>`
**回滚技能到指定版本**
执行步骤:
1. 验证技能存在
2. 进入仓库目录
3. 验证目标版本存在(`git tag` 或 commit hash
4. 显示版本差异和警告
5. 询问用户确认
6. 执行 `git checkout <version>`
7. 更新 `registry.yaml` 中的版本号
8. 同步文件
9. 执行 `post_update` 钩子
10. 显示回滚结果
---
### `/skill info <name>`
**查看技能详细信息**
执行步骤:
1. 读取 `skill.yaml`
2. 读取 git 仓库信息(当前分支、最新 commit
3. 显示详细信息
输出格式:
```
技能: coolbuy-paas
版本: v1.2.0
描述: 酷采3.0 SaaS 租户端开发与测试
作者: your-team
状态: active
仓库: git@github.com:org/skill-coolbuy-paas.git
分支: main
Commit: abc1234 (2026-01-20)
依赖:
- dev-coding
- ops-tools
触发关键词:
- coolbuy-paas
- 酷采
- 商品管理
```
---
### `/skill check`
**检查所有技能是否有更新**
执行步骤:
1. 遍历所有已安装技能
2. 对每个技能执行 `git fetch origin`
3. 比较本地和远程 HEAD
4. 显示有更新的技能列表
输出格式:
```
检查技能更新...
有更新的技能 (2个):
- coolbuy-paas: v1.1.0 → v1.2.0
- req: v4.4.0 → v4.5.0
执行 `/skill upgrade` 升级所有技能
```
---
## 目录结构
```
~/.claude/
├── skills/
│ ├── registry.yaml # 技能注册表
│ ├── repos/ # Git 仓库存储目录
│ │ ├── coolbuy-paas/
│ │ │ ├── .git/
│ │ │ ├── skill.yaml
│ │ │ └── SKILL.md
│ │ └── ...
│ ├── coolbuy-paas/ # 技能目录(符号链接或复制)
│ │ └── SKILL.md
│ ├── skill-manager/ # 本技能
│ │ ├── skill.yaml
│ │ ├── SKILL.md
│ │ └── scripts/
│ └── ...
└── settings.json
```
---
## Registry 结构
`~/.claude/skills/registry.yaml`:
```yaml
version: 1
updated_at: 2026-01-26T12:00:00Z
config:
auto_update_check: true
default_branch: main
skills:
coolbuy-paas:
repo: git@github.com:org/skill-coolbuy-paas.git
local_path: ~/.claude/skills/repos/coolbuy-paas
version: 1.2.0
installed_at: 2026-01-15T10:00:00Z
last_updated: 2026-01-20T10:30:00Z
status: active
```
---
## skill.yaml 规范
```yaml
# 必填
name: skill-name # hyphen-case, ≤64字符
version: 1.0.0 # 语义化版本
description: 技能描述 # ≤1024字符
# 可选
author: author-name
license: MIT
triggers:
keywords: [关键词列表]
commands: [/command]
file_patterns: ["**/pattern/**"]
prompt_file: SKILL.md
dependencies:
- other-skill
mcp_servers:
- server-name
hooks:
post_install: [命令列表]
post_update: [命令列表]
post_uninstall: [命令列表]
allowed_tools:
- Bash
- Read
metadata:
category: category
tags: [tag1, tag2]
```
---
## 脚本工具
### scripts/validate.sh
验证 skill.yaml 格式:
```bash
~/.claude/skills/skill-manager/scripts/validate.sh <skill-directory>
```
### scripts/sync.sh
同步技能文件:
```bash
~/.claude/skills/skill-manager/scripts/sync.sh <skill-name>
```
---
## 注意事项
1. **私有仓库**: 需要配置 SSH key 或 Personal Access Token
2. **依赖循环**: 安装前会检测循环依赖
3. **版本兼容**: 支持语义化版本和 Git commit hash
4. **钩子安全**: 钩子脚本在执行前会显示内容供用户确认

View File

@@ -0,0 +1,88 @@
#!/bin/bash
# validate.sh - 验证 skill.yaml 格式
# 用法: ./validate.sh <skill-directory>
set -e
SKILL_DIR="${1:-.}"
SKILL_YAML="$SKILL_DIR/skill.yaml"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo "验证技能目录: $SKILL_DIR"
echo "----------------------------------------"
# 检查 skill.yaml 是否存在
if [ ! -f "$SKILL_YAML" ]; then
echo -e "${RED}✗ skill.yaml 不存在${NC}"
exit 1
fi
echo -e "${GREEN}✓ skill.yaml 存在${NC}"
# 检查 YAML 格式(需要 yq 或 python
if command -v yq &> /dev/null; then
if ! yq e '.' "$SKILL_YAML" > /dev/null 2>&1; then
echo -e "${RED}✗ YAML 格式错误${NC}"
exit 1
fi
echo -e "${GREEN}✓ YAML 格式正确${NC}"
# 检查必填字段
NAME=$(yq e '.name' "$SKILL_YAML")
VERSION=$(yq e '.version' "$SKILL_YAML")
DESCRIPTION=$(yq e '.description' "$SKILL_YAML")
if [ "$NAME" == "null" ] || [ -z "$NAME" ]; then
echo -e "${RED}✗ 缺少必填字段: name${NC}"
exit 1
fi
echo -e "${GREEN}✓ name: $NAME${NC}"
if [ "$VERSION" == "null" ] || [ -z "$VERSION" ]; then
echo -e "${RED}✗ 缺少必填字段: version${NC}"
exit 1
fi
echo -e "${GREEN}✓ version: $VERSION${NC}"
if [ "$DESCRIPTION" == "null" ] || [ -z "$DESCRIPTION" ]; then
echo -e "${RED}✗ 缺少必填字段: description${NC}"
exit 1
fi
echo -e "${GREEN}✓ description: 已设置${NC}"
# 验证 name 格式
if ! echo "$NAME" | grep -qE '^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$'; then
echo -e "${RED}✗ name 格式错误: 只能包含小写字母、数字、连字符${NC}"
exit 1
fi
if [ ${#NAME} -gt 64 ]; then
echo -e "${RED}✗ name 超过 64 字符${NC}"
exit 1
fi
echo -e "${GREEN}✓ name 格式正确${NC}"
# 检查 prompt_file
PROMPT_FILE=$(yq e '.prompt_file // "SKILL.md"' "$SKILL_YAML")
if [ ! -f "$SKILL_DIR/$PROMPT_FILE" ]; then
echo -e "${YELLOW}⚠ prompt_file 不存在: $PROMPT_FILE${NC}"
else
echo -e "${GREEN}✓ prompt_file: $PROMPT_FILE${NC}"
fi
elif command -v python3 &> /dev/null; then
python3 -c "import yaml; yaml.safe_load(open('$SKILL_YAML'))" 2>/dev/null
if [ $? -ne 0 ]; then
echo -e "${RED}✗ YAML 格式错误${NC}"
exit 1
fi
echo -e "${GREEN}✓ YAML 格式正确 (使用 Python 验证)${NC}"
else
echo -e "${YELLOW}⚠ 无法验证 YAML 格式 (需要 yq 或 python3)${NC}"
fi
echo "----------------------------------------"
echo -e "${GREEN}✓ 验证通过${NC}"