Add skills
This commit is contained in:
236
.config/opencode/skills/skill-migrator/SKILL.md
Normal file
236
.config/opencode/skills/skill-migrator/SKILL.md
Normal file
@@ -0,0 +1,236 @@
|
||||
---
|
||||
name: skill-migrator
|
||||
description: This skill should be used when the user asks to "migrate skills from a directory", "import tools to opencode", "move llm tools", "copy skills from a path", "transfer skills to opencode", "install skills from a directory", or migrate LLM tools between directories
|
||||
license: MIT
|
||||
compatibility: opencode
|
||||
metadata:
|
||||
category: tools
|
||||
version: "1.0.0"
|
||||
---
|
||||
|
||||
# Skill Migrator
|
||||
|
||||
Migrate LLM tools and skills from any directory to OpenCode's skills directory. Supports both global (`~/.config/opencode/skills/`) and local project (`.opencode/skills/`) installations.
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides a streamlined way to import skills from external sources or backup locations into OpenCode. It handles skill discovery, conflict resolution, and installation to the appropriate location.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Migrate Skills from a Directory
|
||||
|
||||
Import all skills from a source directory:
|
||||
|
||||
```bash
|
||||
scripts/migrate.sh /path/to/source/skills
|
||||
```
|
||||
|
||||
The script will:
|
||||
1. Recursively discover all skills (directories containing `SKILL.md`)
|
||||
2. List found skills and ask which to migrate
|
||||
3. Handle conflicts interactively (overwrite/skip/backup)
|
||||
4. Install to global skills directory by default
|
||||
|
||||
### Non-Interactive Mode (Automation-Friendly)
|
||||
|
||||
For use with opencode or automated workflows, use the non-interactive flags:
|
||||
|
||||
```bash
|
||||
# Migrate all skills without prompts
|
||||
scripts/migrate.sh /path/to/source/skills --all --yes
|
||||
|
||||
# Migrate all, skip existing skills (no overwrite)
|
||||
scripts/migrate.sh /path/to/source/skills --all --yes --conflict-strategy skip
|
||||
|
||||
# Migrate all, overwrite existing skills
|
||||
scripts/migrate.sh /path/to/source/skills --all --yes --conflict-strategy overwrite
|
||||
|
||||
# Migrate all, backup existing before overwriting
|
||||
scripts/migrate.sh /path/to/source/skills --all --yes --conflict-strategy backup
|
||||
```
|
||||
|
||||
### Install to Local Project
|
||||
|
||||
Migrate skills to the current project's local skills directory:
|
||||
|
||||
```bash
|
||||
# Interactive mode
|
||||
scripts/migrate.sh /path/to/source/skills --local
|
||||
|
||||
# Non-interactive mode
|
||||
scripts/migrate.sh /path/to/source/skills --local --all --yes
|
||||
```
|
||||
|
||||
### Dry Run (Preview)
|
||||
|
||||
See what would be migrated without making changes:
|
||||
|
||||
```bash
|
||||
# Preview only
|
||||
scripts/migrate.sh /path/to/source/skills --dry-run
|
||||
|
||||
# Preview with non-interactive flags
|
||||
scripts/migrate.sh /path/to/source/skills --all --dry-run
|
||||
```
|
||||
|
||||
### Migrate Specific Skills
|
||||
|
||||
After discovery, the script presents an interactive menu to select which skills to migrate. Choose specific skills or migrate all discovered skills.
|
||||
|
||||
## Migration Process
|
||||
|
||||
### Step 1: Discovery
|
||||
|
||||
The script searches the source directory recursively for skills:
|
||||
- Looks for directories containing `SKILL.md`
|
||||
- Lists all discovered skills with their paths
|
||||
|
||||
### Step 2: Selection
|
||||
|
||||
Interactive selection menu:
|
||||
- View all discovered skills
|
||||
- Select individual skills or all
|
||||
- Confirm before proceeding
|
||||
|
||||
Use `--all` flag to skip selection and migrate all discovered skills.
|
||||
|
||||
### Step 3: Conflict Resolution
|
||||
|
||||
For each skill that already exists at the destination:
|
||||
- **Overwrite**: Replace existing skill
|
||||
- **Skip**: Keep existing skill, skip this one
|
||||
- **Backup**: Create backup with timestamp, then overwrite
|
||||
|
||||
Use `--conflict-strategy <strategy>` to set a default strategy and avoid prompts.
|
||||
|
||||
### Step 4: Installation
|
||||
|
||||
Copies selected skills to destination:
|
||||
- Preserves directory structure
|
||||
- Maintains file permissions
|
||||
- Copies all skill contents as-is
|
||||
|
||||
### Step 5: Report
|
||||
|
||||
Generates a migration report showing:
|
||||
- Successfully migrated skills
|
||||
- Skipped skills (with reason)
|
||||
- Backed up skills (with backup location)
|
||||
- Any errors encountered
|
||||
|
||||
## Scripts
|
||||
|
||||
Use the migration script:
|
||||
|
||||
- `scripts/migrate.sh` - Main migration script with interactive and non-interactive workflow
|
||||
|
||||
### Script Usage
|
||||
|
||||
```bash
|
||||
scripts/migrate.sh [OPTIONS] <source-directory>
|
||||
|
||||
Options:
|
||||
-a, --all Migrate all discovered skills without prompting
|
||||
-c, --conflict-strategy Set conflict resolution: skip, overwrite, backup
|
||||
-y, --yes Auto-confirm all prompts without interaction
|
||||
-l, --local Install to local project (.opencode/skills/)
|
||||
-g, --global Install to global directory (~/.config/opencode/skills/)
|
||||
-d, --dry-run Preview changes without migrating
|
||||
-h, --help Show help message
|
||||
|
||||
Interactive Mode (default):
|
||||
The script will prompt for skill selection, conflict resolution, and confirmation.
|
||||
|
||||
Non-Interactive Mode:
|
||||
Use -a/--all, -y/--yes, and -c/--conflict-strategy for automated usage.
|
||||
|
||||
Examples:
|
||||
# Interactive mode
|
||||
scripts/migrate.sh ~/my-skills
|
||||
|
||||
# Non-interactive: migrate all
|
||||
scripts/migrate.sh ~/my-skills --all --yes
|
||||
|
||||
# Non-interactive: migrate all, skip existing
|
||||
scripts/migrate.sh ~/my-skills --all --yes --conflict-strategy skip
|
||||
|
||||
# Non-interactive: migrate all, overwrite existing
|
||||
scripts/migrate.sh ~/my-skills --all --yes --conflict-strategy overwrite
|
||||
|
||||
# Dry run
|
||||
scripts/migrate.sh ~/my-skills --all --dry-run
|
||||
|
||||
# Local project installation
|
||||
scripts/migrate.sh ~/my-skills --local --all --yes
|
||||
```
|
||||
|
||||
## Target Locations
|
||||
|
||||
### Global Skills (Default)
|
||||
|
||||
Installed to: `~/.config/opencode/skills/`
|
||||
|
||||
- Available across all projects
|
||||
- Shared OpenCode installation
|
||||
- Best for commonly used skills
|
||||
|
||||
### Local Project Skills
|
||||
|
||||
Installed to: `./.opencode/skills/`
|
||||
|
||||
- Project-specific skills only
|
||||
- Version controlled with project
|
||||
- Team-shared within project
|
||||
|
||||
## Non-Interactive Mode
|
||||
|
||||
For use with opencode and coding agents, the script supports full automation:
|
||||
|
||||
### Flags for Automation
|
||||
|
||||
- `--all`: Skip skill selection and migrate all discovered skills
|
||||
- `--yes`: Skip confirmation prompts and proceed automatically
|
||||
- `--conflict-strategy <strategy>`: Set default conflict handling (skip/overwrite/backup)
|
||||
|
||||
### Example Automation Scenarios
|
||||
|
||||
**Migrate all skills, skip existing:**
|
||||
```bash
|
||||
scripts/migrate.sh ~/my-skills --all --yes --conflict-strategy skip
|
||||
```
|
||||
|
||||
**Migrate all skills, overwrite existing:**
|
||||
```bash
|
||||
scripts/migrate.sh ~/my-skills --all --yes --conflict-strategy overwrite
|
||||
```
|
||||
|
||||
**Migrate all skills, backup existing:**
|
||||
```bash
|
||||
scripts/migrate.sh ~/my-skills --all --yes --conflict-strategy backup
|
||||
```
|
||||
|
||||
**Dry run to preview changes:**
|
||||
```bash
|
||||
scripts/migrate.sh ~/my-skills --all --dry-run
|
||||
```
|
||||
|
||||
### Conflict Strategies
|
||||
|
||||
- `skip`: Keep existing skills, skip migrating those that already exist
|
||||
- `overwrite`: Replace existing skills with the new versions
|
||||
- `backup`: Create timestamped backups of existing skills before overwriting
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Skills are copied as-is without validation
|
||||
- File permissions are preserved during migration
|
||||
- Backups are stored with timestamp: `skillname.backup.YYYYMMDD_HHMMSS`
|
||||
- Empty directories and non-skill folders are ignored
|
||||
- The script requires write permissions to the destination directory
|
||||
- Use `--all`, `--yes`, and `--conflict-strategy` flags for non-interactive usage with opencode
|
||||
- Non-interactive mode is ideal for automated workflows and CI/CD pipelines
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See `references/migration-guide.md` for detailed troubleshooting and advanced usage.
|
||||
62
.config/opencode/skills/skill-migrator/evals/evals.json
Normal file
62
.config/opencode/skills/skill-migrator/evals/evals.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"skill_name": "skill-migrator",
|
||||
"evals": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "basic-non-interactive-migration",
|
||||
"prompt": "Migrate all skills from ~/.agents to the global opencode skills directory without any interactive prompts. Use the skill-migrator skill.",
|
||||
"expected_output": "The agent should use the migrate.sh script with --all and --yes flags to perform a non-interactive migration. No prompts should be displayed.",
|
||||
"assertions": [
|
||||
"Agent identifies the correct script path",
|
||||
"Agent uses --all flag to select all skills",
|
||||
"Agent uses --yes flag to auto-confirm",
|
||||
"Migration completes without interactive prompts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "migration-with-conflict-resolution",
|
||||
"prompt": "Migrate skills from ~/.agents to the global skills directory. Some skills may already exist at the destination. Use the skill-migrator to handle conflicts by skipping existing skills automatically.",
|
||||
"expected_output": "The agent should use --conflict-strategy skip to automatically handle conflicts without prompting.",
|
||||
"assertions": [
|
||||
"Agent uses --conflict-strategy skip flag",
|
||||
"Agent includes --all and --yes for non-interactive mode",
|
||||
"Existing skills are skipped without prompts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "dry-run-preview",
|
||||
"prompt": "Preview what would be migrated from ~/.agents without actually making any changes. Show what skills would be migrated and how conflicts would be handled.",
|
||||
"expected_output": "The agent should use --dry-run flag to preview changes without making them.",
|
||||
"assertions": [
|
||||
"Agent uses --dry-run flag",
|
||||
"Agent may also use --all to preview all skills",
|
||||
"No actual file changes are made",
|
||||
"Preview output shows discovered skills"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "local-project-installation",
|
||||
"prompt": "Migrate skills from ~/.agents to the local project directory (.opencode/skills/) instead of the global directory. Make it non-interactive.",
|
||||
"expected_output": "The agent should use --local flag along with non-interactive flags.",
|
||||
"assertions": [
|
||||
"Agent uses --local flag",
|
||||
"Agent uses --all and --yes for non-interactive mode",
|
||||
"Target directory is the local .opencode/skills/ directory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "backup-conflict-strategy",
|
||||
"prompt": "Migrate skills from ~/.agents. If any skills already exist, create backups of the existing ones before overwriting them. Do this without any prompts.",
|
||||
"expected_output": "The agent should use --conflict-strategy backup to create backups before overwriting.",
|
||||
"assertions": [
|
||||
"Agent uses --conflict-strategy backup",
|
||||
"Agent includes --all and --yes",
|
||||
"Existing skills are backed up with timestamps"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
231
.config/opencode/skills/skill-migrator/evals/run-tests.sh
Executable file
231
.config/opencode/skills/skill-migrator/evals/run-tests.sh
Executable file
@@ -0,0 +1,231 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Skill Migrator Test Script
|
||||
# Tests the non-interactive mode functionality
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MIGRATE_SCRIPT="$HOME/.config/opencode/skills/skill-migrator/scripts/migrate.sh"
|
||||
TEST_DIR="$HOME/.agents-test"
|
||||
|
||||
echo "========================================"
|
||||
echo " SKILL MIGRATOR TEST SUITE"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# Setup: Create test skills
|
||||
setup_test_env() {
|
||||
echo "Setting up test environment..."
|
||||
rm -rf "$TEST_DIR"
|
||||
mkdir -p "$TEST_DIR"
|
||||
|
||||
# Create a test skill
|
||||
mkdir -p "$TEST_DIR/test-skill-1"
|
||||
cat > "$TEST_DIR/test-skill-1/SKILL.md" << 'EOF'
|
||||
---
|
||||
name: test-skill-1
|
||||
description: Test skill 1 for migration testing
|
||||
---
|
||||
|
||||
# Test Skill 1
|
||||
|
||||
This is a test skill.
|
||||
EOF
|
||||
|
||||
# Create another test skill
|
||||
mkdir -p "$TEST_DIR/test-skill-2"
|
||||
cat > "$TEST_DIR/test-skill-2/SKILL.md" << 'EOF'
|
||||
---
|
||||
name: test-skill-2
|
||||
description: Test skill 2 for migration testing
|
||||
---
|
||||
|
||||
# Test Skill 2
|
||||
|
||||
This is another test skill.
|
||||
EOF
|
||||
|
||||
echo "✓ Test environment created"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Test 1: Basic non-interactive migration
|
||||
test_basic_migration() {
|
||||
echo "Test 1: Basic Non-Interactive Migration"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Clean target
|
||||
rm -rf "$HOME/.config/opencode/skills/test-skill-1"
|
||||
rm -rf "$HOME/.config/opencode/skills/test-skill-2"
|
||||
|
||||
# Run migration with non-interactive flags
|
||||
if "$MIGRATE_SCRIPT" "$TEST_DIR" --all --yes; then
|
||||
echo "✓ Migration completed successfully"
|
||||
else
|
||||
echo "✗ Migration failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify skills were migrated
|
||||
if [[ -d "$HOME/.config/opencode/skills/test-skill-1" && -d "$HOME/.config/opencode/skills/test-skill-2" ]]; then
|
||||
echo "✓ Skills migrated to correct location"
|
||||
else
|
||||
echo "✗ Skills not found in target directory"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Test 2: Skip conflict strategy
|
||||
test_skip_strategy() {
|
||||
echo "Test 2: Skip Conflict Strategy"
|
||||
echo "-------------------------------"
|
||||
|
||||
# Modify source skill
|
||||
echo "# Modified" >> "$TEST_DIR/test-skill-1/SKILL.md"
|
||||
|
||||
# Run migration with skip strategy
|
||||
if "$MIGRATE_SCRIPT" "$TEST_DIR" --all --yes --conflict-strategy skip; then
|
||||
echo "✓ Migration with skip strategy completed"
|
||||
else
|
||||
echo "✗ Migration failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify original skill was NOT overwritten
|
||||
if ! grep -q "# Modified" "$HOME/.config/opencode/skills/test-skill-1/SKILL.md"; then
|
||||
echo "✓ Existing skill was skipped (not overwritten)"
|
||||
else
|
||||
echo "✗ Existing skill was overwritten (should have been skipped)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Test 3: Overwrite conflict strategy
|
||||
test_overwrite_strategy() {
|
||||
echo "Test 3: Overwrite Conflict Strategy"
|
||||
echo "------------------------------------"
|
||||
|
||||
# Run migration with overwrite strategy
|
||||
if "$MIGRATE_SCRIPT" "$TEST_DIR" --all --yes --conflict-strategy overwrite; then
|
||||
echo "✓ Migration with overwrite strategy completed"
|
||||
else
|
||||
echo "✗ Migration failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify skill WAS overwritten
|
||||
if grep -q "# Modified" "$HOME/.config/opencode/skills/test-skill-1/SKILL.md"; then
|
||||
echo "✓ Existing skill was overwritten"
|
||||
else
|
||||
echo "✗ Existing skill was not overwritten"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Test 4: Backup conflict strategy
|
||||
test_backup_strategy() {
|
||||
echo "Test 4: Backup Conflict Strategy"
|
||||
echo "---------------------------------"
|
||||
|
||||
# Run migration with backup strategy
|
||||
if "$MIGRATE_SCRIPT" "$TEST_DIR" --all --yes --conflict-strategy backup; then
|
||||
echo "✓ Migration with backup strategy completed"
|
||||
else
|
||||
echo "✗ Migration failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify backup was created
|
||||
if ls "$HOME/.config/opencode/skills/test-skill-1.backup."* 1> /dev/null 2>&1; then
|
||||
echo "✓ Backup was created"
|
||||
else
|
||||
echo "✗ Backup not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Test 5: Dry run
|
||||
test_dry_run() {
|
||||
echo "Test 5: Dry Run Mode"
|
||||
echo "---------------------"
|
||||
|
||||
# Create a new test skill that doesn't exist yet
|
||||
mkdir -p "$TEST_DIR/test-skill-3"
|
||||
cat > "$TEST_DIR/test-skill-3/SKILL.md" << 'EOF'
|
||||
---
|
||||
name: test-skill-3
|
||||
description: Test skill 3 for dry run testing
|
||||
---
|
||||
|
||||
# Test Skill 3
|
||||
|
||||
This skill should not be migrated in dry run.
|
||||
EOF
|
||||
|
||||
# Run migration with dry-run
|
||||
if "$MIGRATE_SCRIPT" "$TEST_DIR" --all --dry-run; then
|
||||
echo "✓ Dry run completed"
|
||||
else
|
||||
echo "✗ Dry run failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify skill was NOT actually migrated
|
||||
if [[ ! -d "$HOME/.config/opencode/skills/test-skill-3" ]]; then
|
||||
echo "✓ Dry run correctly made no changes"
|
||||
else
|
||||
echo "✗ Skill was migrated (should not happen in dry run)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Cleanup
|
||||
cleanup() {
|
||||
echo "Cleaning up..."
|
||||
rm -rf "$TEST_DIR"
|
||||
rm -rf "$HOME/.config/opencode/skills/test-skill-1"
|
||||
rm -rf "$HOME/.config/opencode/skills/test-skill-2"
|
||||
rm -rf "$HOME/.config/opencode/skills/test-skill-3"
|
||||
rm -f "$HOME/.config/opencode/skills/test-skill-1.backup."*
|
||||
echo "✓ Cleanup complete"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run all tests
|
||||
main() {
|
||||
setup_test_env
|
||||
|
||||
local failed=0
|
||||
|
||||
test_basic_migration || ((failed++))
|
||||
test_skip_strategy || ((failed++))
|
||||
test_overwrite_strategy || ((failed++))
|
||||
test_backup_strategy || ((failed++))
|
||||
test_dry_run || ((failed++))
|
||||
|
||||
cleanup
|
||||
|
||||
echo "========================================"
|
||||
echo " TEST RESULTS"
|
||||
echo "========================================"
|
||||
if [[ $failed -eq 0 ]]; then
|
||||
echo "✓ All tests passed!"
|
||||
exit 0
|
||||
else
|
||||
echo "✗ $failed test(s) failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,438 @@
|
||||
# Skill Migration Guide
|
||||
|
||||
Comprehensive guide for migrating LLM tools and skills to OpenCode.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Quick Start](#quick-start)
|
||||
2. [Usage Examples](#usage-examples)
|
||||
3. [Interactive Workflow](#interactive-workflow)
|
||||
4. [Conflict Resolution](#conflict-resolution)
|
||||
5. [Troubleshooting](#troubleshooting)
|
||||
6. [Advanced Usage](#advanced-usage)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Migration
|
||||
|
||||
Migrate all skills from a directory to global OpenCode:
|
||||
|
||||
```bash
|
||||
# Discover and migrate skills
|
||||
~/.config/opencode/skills/skill-migrator/scripts/migrate.sh /path/to/skills
|
||||
|
||||
# Or if you're in the skill-migrator directory
|
||||
./scripts/migrate.sh /path/to/skills
|
||||
```
|
||||
|
||||
### Migrate to Local Project
|
||||
|
||||
```bash
|
||||
./scripts/migrate.sh /path/to/skills --local
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Migrating from Backup
|
||||
|
||||
You have a backup of skills in `~/backups/opencode-skills/`:
|
||||
|
||||
```bash
|
||||
./scripts/migrate.sh ~/backups/opencode-skills
|
||||
```
|
||||
|
||||
**Workflow:**
|
||||
1. Script discovers all skills containing `SKILL.md`
|
||||
2. Lists discovered skills with numbers
|
||||
3. You select which to migrate (or 'a' for all)
|
||||
4. For existing skills, choose: overwrite/skip/backup
|
||||
5. Migration completes with a report
|
||||
|
||||
### Example 2: Preview Before Migrating
|
||||
|
||||
See what would be migrated without making changes:
|
||||
|
||||
```bash
|
||||
./scripts/migrate.sh ~/external-tools --dry-run
|
||||
```
|
||||
|
||||
This shows:
|
||||
- Which skills would be discovered
|
||||
- Which already exist at destination
|
||||
- No actual changes are made
|
||||
|
||||
### Example 3: Selective Migration
|
||||
|
||||
Migrate only specific skills:
|
||||
|
||||
```bash
|
||||
./scripts/migrate.sh ~/my-tools
|
||||
# At selection prompt, enter: 1,3,5
|
||||
# Only skills 1, 3, and 5 will be migrated
|
||||
```
|
||||
|
||||
### Example 4: Local Development
|
||||
|
||||
Working on a project that needs specific skills:
|
||||
|
||||
```bash
|
||||
# In your project directory
|
||||
./scripts/migrate.sh ~/project-skills --local
|
||||
|
||||
# Skills installed to ./.opencode/skills/
|
||||
# Can be version controlled with your project
|
||||
```
|
||||
|
||||
## Interactive Workflow
|
||||
|
||||
### Discovery Phase
|
||||
|
||||
The script searches recursively for directories containing `SKILL.md`:
|
||||
|
||||
```
|
||||
ℹ Discovering skills in: /home/user/external-skills
|
||||
✓ Found 5 skill(s)
|
||||
|
||||
Discovered Skills:
|
||||
==================
|
||||
1. docker-helper (/home/user/external-skills/docker-helper)
|
||||
2. git-workflow (/home/user/external-skills/workflows/git-workflow)
|
||||
3. api-tester (/home/user/external-skills/devtools/api-tester)
|
||||
4. doc-generator (/home/user/external-skills/docs/doc-generator)
|
||||
5. test-validator (/home/user/external-skills/qa/test-validator)
|
||||
```
|
||||
|
||||
### Selection Phase
|
||||
|
||||
Choose which skills to migrate:
|
||||
|
||||
```
|
||||
Select skills to migrate:
|
||||
[a] - Migrate all
|
||||
[1-5] - Select specific skill(s) by number (comma-separated)
|
||||
[q] - Quit
|
||||
|
||||
Your choice: 1,3,5
|
||||
```
|
||||
|
||||
### Confirmation
|
||||
|
||||
Before proceeding:
|
||||
|
||||
```
|
||||
Ready to migrate 3 skill(s).
|
||||
Continue? [Y/n]: Y
|
||||
```
|
||||
|
||||
### Migration Progress
|
||||
|
||||
Live progress as skills are migrated:
|
||||
|
||||
```
|
||||
ℹ Migrating: docker-helper
|
||||
⚠ Skill already exists: docker-helper
|
||||
Location: ~/.config/opencode/skills/docker-helper
|
||||
|
||||
Options:
|
||||
[o] - Overwrite (replace existing)
|
||||
[s] - Skip (keep existing)
|
||||
[b] - Backup (create backup, then overwrite)
|
||||
|
||||
Your choice [o/s/b]: b
|
||||
ℹ Backed up to: ~/.config/opencode/skills/docker-helper.backup.20240319_143052
|
||||
✓ Migrated: docker-helper
|
||||
|
||||
ℹ Migrating: api-tester
|
||||
✓ Migrated: api-tester
|
||||
|
||||
ℹ Migrating: test-validator
|
||||
✓ Migrated: test-validator
|
||||
```
|
||||
|
||||
### Final Report
|
||||
|
||||
```
|
||||
========================================
|
||||
MIGRATION REPORT
|
||||
========================================
|
||||
|
||||
✓ Successfully Migrated (3):
|
||||
• docker-helper
|
||||
• api-tester
|
||||
• test-validator
|
||||
|
||||
ℹ Backed Up (1):
|
||||
• docker-helper -> ~/.config/opencode/skills/docker-helper.backup.20240319_143052
|
||||
|
||||
Target Directory: ~/.config/opencode/skills
|
||||
========================================
|
||||
```
|
||||
|
||||
## Conflict Resolution
|
||||
|
||||
### When Conflicts Occur
|
||||
|
||||
A conflict happens when a skill with the same name already exists at the destination.
|
||||
|
||||
### Options
|
||||
|
||||
#### 1. Overwrite
|
||||
- **When to use:** The new version is newer/better, or you don't need the old one
|
||||
- **Result:** Existing skill is completely replaced
|
||||
- **Risk:** You lose the old version permanently
|
||||
|
||||
#### 2. Skip
|
||||
- **When to use:** You want to keep the existing skill as-is
|
||||
- **Result:** New skill is not migrated
|
||||
- **Use case:** The existing skill has customizations you want to keep
|
||||
|
||||
#### 3. Backup
|
||||
- **When to use:** You want both versions (safe option)
|
||||
- **Result:** Old skill backed up with timestamp, new skill installed
|
||||
- **Backup location:** `~/.config/opencode/skills/skillname.backup.YYYYMMDD_HHMMSS`
|
||||
|
||||
### Best Practices
|
||||
|
||||
- **Always choose backup** when unsure - you can manually merge later
|
||||
- **Use skip** if you've customized the existing skill
|
||||
- **Use overwrite** if the source is definitely newer/correct
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: "No skills found"
|
||||
|
||||
**Symptoms:**
|
||||
```
|
||||
⚠ No skills found in: /path/to/source
|
||||
ℹ Looking for directories containing 'SKILL.md'
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Verify source structure:**
|
||||
```bash
|
||||
# Check if SKILL.md files exist
|
||||
find /path/to/source -name "SKILL.md" -type f
|
||||
```
|
||||
|
||||
2. **Check file permissions:**
|
||||
```bash
|
||||
# Ensure you can read the directory
|
||||
ls -la /path/to/source
|
||||
```
|
||||
|
||||
3. **Verify path is correct:**
|
||||
```bash
|
||||
# Make sure path exists
|
||||
ls -d /path/to/source
|
||||
```
|
||||
|
||||
### Issue: "Permission denied"
|
||||
|
||||
**Symptoms:**
|
||||
```
|
||||
✗ Cannot write to target directory
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Check directory permissions:**
|
||||
```bash
|
||||
ls -ld ~/.config/opencode/skills
|
||||
```
|
||||
|
||||
2. **Fix permissions:**
|
||||
```bash
|
||||
# For global directory
|
||||
mkdir -p ~/.config/opencode/skills
|
||||
chmod 755 ~/.config/opencode/skills
|
||||
|
||||
# For local directory
|
||||
mkdir -p .opencode/skills
|
||||
chmod 755 .opencode/skills
|
||||
```
|
||||
|
||||
3. **Run with appropriate user:**
|
||||
Ensure you're running as the user who owns the OpenCode config
|
||||
|
||||
### Issue: Script hangs or no prompt
|
||||
|
||||
**Symptoms:** Script stops without showing selection menu
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Ensure terminal is interactive:**
|
||||
```bash
|
||||
# Check if stdin is a terminal
|
||||
[ -t 0 ] && echo "Interactive" || echo "Not interactive"
|
||||
```
|
||||
|
||||
2. **Run in proper terminal:**
|
||||
Don't run from non-interactive contexts (CI/CD, scripts without TTY)
|
||||
|
||||
3. **Use --dry-run first:**
|
||||
```bash
|
||||
./scripts/migrate.sh /path --dry-run
|
||||
```
|
||||
|
||||
### Issue: Skills not appearing in OpenCode
|
||||
|
||||
**Symptoms:** Migration succeeds but OpenCode doesn't recognize new skills
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Check skill structure:**
|
||||
```bash
|
||||
# Verify SKILL.md exists
|
||||
ls ~/.config/opencode/skills/new-skill/SKILL.md
|
||||
```
|
||||
|
||||
2. **Check SKILL.md format:**
|
||||
- Must start with YAML frontmatter (`---`)
|
||||
- Must have `name:` field
|
||||
- Must have `description:` field (20-1024 chars)
|
||||
|
||||
3. **Validate the skill:**
|
||||
```bash
|
||||
~/.config/opencode/skills/skill-builder/scripts/validate-skill.sh \
|
||||
~/.config/opencode/skills/new-skill
|
||||
```
|
||||
|
||||
4. **Restart OpenCode** if using a GUI/IDE plugin
|
||||
|
||||
### Issue: Backup not created
|
||||
|
||||
**Symptoms:** Chose 'backup' option but can't find backup
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Check backup location:**
|
||||
```bash
|
||||
ls -la ~/.config/opencode/skills/*.backup.*
|
||||
```
|
||||
|
||||
2. **Check timestamp format:**
|
||||
Backups use format: `skillname.backup.YYYYMMDD_HHMMSS`
|
||||
|
||||
3. **Verify not in dry-run:**
|
||||
Backups are only created during actual migration, not dry-run
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Batch Migration Script
|
||||
|
||||
Create a wrapper for recurring migrations:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# migrate-from-backup.sh
|
||||
|
||||
BACKUP_DIR="${HOME}/backups/opencode-skills"
|
||||
MIGRATOR="${HOME}/.config/opencode/skills/skill-migrator/scripts/migrate.sh"
|
||||
|
||||
# Always backup before overwriting
|
||||
export MIGRATOR_DEFAULT_ACTION="backup"
|
||||
|
||||
# Run migration
|
||||
"$MIGRATOR" "$BACKUP_DIR" "$@"
|
||||
```
|
||||
|
||||
### Automated Sync
|
||||
|
||||
Sync skills from a git repository:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# sync-skills.sh
|
||||
|
||||
SKILLS_REPO="https://github.com/company/shared-opencode-skills.git"
|
||||
TEMP_DIR="/tmp/opencode-skills-sync"
|
||||
MIGRATOR="${HOME}/.config/opencode/skills/skill-migrator/scripts/migrate.sh"
|
||||
|
||||
# Clone latest
|
||||
rm -rf "$TEMP_DIR"
|
||||
git clone "$SKILLS_REPO" "$TEMP_DIR"
|
||||
|
||||
# Dry run first
|
||||
"$MIGRATOR" "$TEMP_DIR/skills" --dry-run
|
||||
|
||||
# Ask for confirmation
|
||||
read -p "Proceed with migration? [y/N]: " confirm
|
||||
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||
"$MIGRATOR" "$TEMP_DIR/skills"
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$TEMP_DIR"
|
||||
```
|
||||
|
||||
### Selective Auto-Backup
|
||||
|
||||
Always backup certain critical skills:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# safe-migrate.sh
|
||||
|
||||
CRITICAL_SKILLS=("production-deploy" "database-migration" "security-scan")
|
||||
SOURCE_DIR="$1"
|
||||
MIGRATOR="${HOME}/.config/opencode/skills/skill-migrator/scripts/migrate.sh"
|
||||
|
||||
# Pre-backup critical skills
|
||||
for skill in "${CRITICAL_SKILLS[@]}"; do
|
||||
skill_path="${HOME}/.config/opencode/skills/${skill}"
|
||||
if [[ -d "$skill_path" ]]; then
|
||||
timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||
cp -r "$skill_path" "${skill_path}.pre-migration.${timestamp}"
|
||||
echo "Pre-backed up: $skill"
|
||||
fi
|
||||
done
|
||||
|
||||
# Run migration
|
||||
"$MIGRATOR" "$SOURCE_DIR"
|
||||
```
|
||||
|
||||
### Migration with Validation
|
||||
|
||||
Validate skills after migration:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# migrate-and-validate.sh
|
||||
|
||||
SOURCE_DIR="$1"
|
||||
MIGRATOR="${HOME}/.config/opencode/skills/skill-migrator/scripts/migrate.sh"
|
||||
VALIDATOR="${HOME}/.config/opencode/skills/skill-builder/scripts/validate-skill.sh"
|
||||
TARGET_DIR="${HOME}/.config/opencode/skills"
|
||||
|
||||
# Migrate
|
||||
"$MIGRATOR" "$SOURCE_DIR"
|
||||
|
||||
# Validate all migrated skills
|
||||
echo ""
|
||||
echo "Validating migrated skills..."
|
||||
for skill_dir in "$TARGET_DIR"/*/; do
|
||||
skill_name=$(basename "$skill_dir")
|
||||
if [[ -f "$skill_dir/SKILL.md" ]]; then
|
||||
echo "Validating: $skill_name"
|
||||
if ! "$VALIDATOR" "$skill_dir" 2>/dev/null; then
|
||||
echo "⚠ $skill_name has validation issues"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
## Tips and Best Practices
|
||||
|
||||
1. **Always use --dry-run first** when migrating from a new source
|
||||
2. **Keep backups** until you've verified skills work in OpenCode
|
||||
3. **Use local skills** for project-specific tools
|
||||
4. **Use global skills** for commonly used utilities
|
||||
5. **Document your skills** with clear descriptions for better OpenCode integration
|
||||
6. **Test migrated skills** by asking OpenCode to use them
|
||||
|
||||
## See Also
|
||||
|
||||
- `SKILL.md` - Main skill documentation
|
||||
- `skill-builder` skill - For validating and creating skills
|
||||
- OpenCode documentation for skill development
|
||||
527
.config/opencode/skills/skill-migrator/scripts/migrate.sh
Executable file
527
.config/opencode/skills/skill-migrator/scripts/migrate.sh
Executable file
@@ -0,0 +1,527 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Skill Migrator - Migrate LLM tools to OpenCode skills directory
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
GLOBAL_SKILLS_DIR="${HOME}/.config/opencode/skills"
|
||||
LOCAL_SKILLS_DIR=".opencode/skills"
|
||||
TARGET_MODE="global"
|
||||
DRY_RUN=false
|
||||
|
||||
# Non-interactive mode flags
|
||||
MIGRATE_ALL=false
|
||||
AUTO_CONFIRM=false
|
||||
CONFLICT_STRATEGY=""
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Data structures
|
||||
declare -a DISCOVERED_SKILLS=()
|
||||
declare -a DISCOVERED_PATHS=()
|
||||
declare -a MIGRATED_SKILLS=()
|
||||
declare -a SKIPPED_SKILLS=()
|
||||
declare -a BACKED_UP_SKILLS=()
|
||||
declare -a ERROR_SKILLS=()
|
||||
|
||||
# Show help
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Skill Migrator - Migrate LLM tools to OpenCode
|
||||
|
||||
Usage: $(basename "$0") [OPTIONS] <source-directory>
|
||||
|
||||
Migrate skills from a source directory to OpenCode's skills directory.
|
||||
|
||||
Options:
|
||||
-a, --all Migrate all discovered skills without prompting
|
||||
-c, --conflict-strategy Set conflict resolution strategy: skip, overwrite, backup
|
||||
-y, --yes Auto-confirm all prompts without interaction
|
||||
-l, --local Install to local project (.opencode/skills/)
|
||||
-g, --global Install to global directory (default: ~/.config/opencode/skills/)
|
||||
-d, --dry-run Preview changes without migrating
|
||||
-h, --help Show this help message
|
||||
|
||||
Interactive Options:
|
||||
By default, the script will prompt you to:
|
||||
- Select which skills to migrate
|
||||
- Resolve conflicts (overwrite/skip/backup)
|
||||
- Confirm before proceeding
|
||||
|
||||
Use -a, -y, and -c flags for non-interactive/automated usage.
|
||||
|
||||
Examples:
|
||||
# Interactive mode (default)
|
||||
$(basename "$0") ~/my-skills
|
||||
|
||||
# Non-interactive: migrate all skills
|
||||
$(basename "$0") ~/my-skills --all --yes
|
||||
|
||||
# Non-interactive: migrate all, skip existing
|
||||
$(basename "$0") ~/my-skills --all --yes --conflict-strategy skip
|
||||
|
||||
# Non-interactive: migrate all, overwrite existing
|
||||
$(basename "$0") ~/my-skills --all --yes --conflict-strategy overwrite
|
||||
|
||||
# Dry run (preview only)
|
||||
$(basename "$0") ~/my-skills --all --dry-run
|
||||
|
||||
# Install to local project
|
||||
$(basename "$0") ~/my-skills --local --all --yes
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Print colored output
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
parse_args() {
|
||||
SOURCE_DIR=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-a|--all)
|
||||
MIGRATE_ALL=true
|
||||
shift
|
||||
;;
|
||||
-y|--yes)
|
||||
AUTO_CONFIRM=true
|
||||
shift
|
||||
;;
|
||||
-c|--conflict-strategy)
|
||||
if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then
|
||||
CONFLICT_STRATEGY="$2"
|
||||
# Validate conflict strategy
|
||||
if [[ "$CONFLICT_STRATEGY" != "skip" && "$CONFLICT_STRATEGY" != "overwrite" && "$CONFLICT_STRATEGY" != "backup" ]]; then
|
||||
print_error "Invalid conflict strategy: $CONFLICT_STRATEGY"
|
||||
print_error "Valid options: skip, overwrite, backup"
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
else
|
||||
print_error "--conflict-strategy requires a value (skip, overwrite, backup)"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
-l|--local)
|
||||
TARGET_MODE="local"
|
||||
shift
|
||||
;;
|
||||
-g|--global)
|
||||
TARGET_MODE="global"
|
||||
shift
|
||||
;;
|
||||
-d|--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
print_error "Unknown option: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$SOURCE_DIR" ]]; then
|
||||
SOURCE_DIR="$1"
|
||||
else
|
||||
print_error "Multiple source directories specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$SOURCE_DIR" ]]; then
|
||||
print_error "No source directory specified"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Get target directory
|
||||
get_target_dir() {
|
||||
if [[ "$TARGET_MODE" == "local" ]]; then
|
||||
if [[ ! -d "$LOCAL_SKILLS_DIR" ]]; then
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
mkdir -p "$LOCAL_SKILLS_DIR"
|
||||
fi
|
||||
fi
|
||||
echo "$LOCAL_SKILLS_DIR"
|
||||
else
|
||||
if [[ ! -d "$GLOBAL_SKILLS_DIR" ]]; then
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
mkdir -p "$GLOBAL_SKILLS_DIR"
|
||||
fi
|
||||
fi
|
||||
echo "$GLOBAL_SKILLS_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Discover skills recursively
|
||||
discover_skills() {
|
||||
local source_dir="$1"
|
||||
|
||||
print_info "Discovering skills in: $source_dir"
|
||||
|
||||
# Find all directories containing SKILL.md
|
||||
local found_skills=0
|
||||
while IFS= read -r -d '' skill_path; do
|
||||
local skill_dir
|
||||
skill_dir=$(dirname "$skill_path")
|
||||
local skill_name
|
||||
skill_name=$(basename "$skill_dir")
|
||||
|
||||
# Skip the skill-migrator itself
|
||||
if [[ "$skill_name" == "skill-migrator" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
DISCOVERED_SKILLS+=("$skill_name")
|
||||
DISCOVERED_PATHS+=("$skill_dir")
|
||||
((found_skills++)) || true
|
||||
|
||||
done < <(find "$source_dir" -type f -name "SKILL.md" -print0 2>/dev/null)
|
||||
|
||||
if [[ ${#DISCOVERED_SKILLS[@]} -eq 0 ]]; then
|
||||
print_warning "No skills found in: $source_dir"
|
||||
print_info "Looking for directories containing 'SKILL.md'"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
print_success "Found $found_skills skill(s)"
|
||||
}
|
||||
|
||||
# Display discovered skills
|
||||
display_discovered() {
|
||||
echo ""
|
||||
echo "Discovered Skills:"
|
||||
echo "=================="
|
||||
|
||||
local i=1
|
||||
for skill_name in "${DISCOVERED_SKILLS[@]}"; do
|
||||
printf "%2d. %-30s (%s)\n" "$i" "$skill_name" "${DISCOVERED_PATHS[$((i-1))]}"
|
||||
((i++)) || true
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Prompt user for skill selection
|
||||
select_skills() {
|
||||
# If --all flag is set, select all skills automatically
|
||||
if [[ "$MIGRATE_ALL" == true ]]; then
|
||||
SELECTED_INDICES=($(seq 1 ${#DISCOVERED_SKILLS[@]}))
|
||||
print_info "Auto-selecting all ${#DISCOVERED_SKILLS[@]} skill(s) (--all flag set)"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Select skills to migrate:"
|
||||
echo " [a] - Migrate all"
|
||||
echo " [1-${#DISCOVERED_SKILLS[@]}] - Select specific skill(s) by number (comma-separated)"
|
||||
echo " [q] - Quit"
|
||||
echo ""
|
||||
read -p "Your choice: " choice
|
||||
|
||||
case "$choice" in
|
||||
a|A)
|
||||
SELECTED_INDICES=($(seq 1 ${#DISCOVERED_SKILLS[@]}))
|
||||
;;
|
||||
q|Q)
|
||||
print_info "Migration cancelled"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
SELECTED_INDICES=()
|
||||
IFS=',' read -ra selections <<< "$choice"
|
||||
for sel in "${selections[@]}"; do
|
||||
sel=$(echo "$sel" | tr -d ' ')
|
||||
if [[ "$sel" =~ ^[0-9]+$ ]] && [[ "$sel" -ge 1 ]] && [[ "$sel" -le ${#DISCOVERED_SKILLS[@]} ]]; then
|
||||
SELECTED_INDICES+=("$sel")
|
||||
else
|
||||
print_warning "Invalid selection: $sel"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#SELECTED_INDICES[@]} -eq 0 ]]; then
|
||||
print_error "No valid skills selected"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Resolve conflict for existing skill
|
||||
resolve_conflict() {
|
||||
local skill_name="$1"
|
||||
local target_dir="$2"
|
||||
local target_path="$target_dir/$skill_name"
|
||||
|
||||
if [[ ! -d "$target_path" ]]; then
|
||||
echo "migrate"
|
||||
return
|
||||
fi
|
||||
|
||||
# If conflict strategy is set via flag, use it
|
||||
if [[ -n "$CONFLICT_STRATEGY" ]]; then
|
||||
# Print to stderr so it doesn't get captured in command substitution
|
||||
print_info "Conflict for '$skill_name': using --conflict-strategy=$CONFLICT_STRATEGY" >&2
|
||||
echo "$CONFLICT_STRATEGY"
|
||||
return
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_warning "Skill already exists: $skill_name"
|
||||
echo " Location: $target_path"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " [o] - Overwrite (replace existing)"
|
||||
echo " [s] - Skip (keep existing)"
|
||||
echo " [b] - Backup (create backup, then overwrite)"
|
||||
echo ""
|
||||
|
||||
while true; do
|
||||
read -p "Your choice [o/s/b]: " conflict_choice
|
||||
case "$conflict_choice" in
|
||||
o|O)
|
||||
echo "overwrite"
|
||||
return
|
||||
;;
|
||||
s|S)
|
||||
echo "skip"
|
||||
return
|
||||
;;
|
||||
b|B)
|
||||
echo "backup"
|
||||
return
|
||||
;;
|
||||
*)
|
||||
print_warning "Invalid choice. Please enter o, s, or b"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Backup existing skill
|
||||
backup_skill() {
|
||||
local skill_name="$1"
|
||||
local target_dir="$2"
|
||||
local target_path="$target_dir/$skill_name"
|
||||
local timestamp
|
||||
timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||
local backup_path="${target_path}.backup.${timestamp}"
|
||||
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
mv "$target_path" "$backup_path"
|
||||
fi
|
||||
|
||||
BACKED_UP_SKILLS+=("$skill_name -> $backup_path")
|
||||
echo "$backup_path"
|
||||
}
|
||||
|
||||
# Migrate a single skill
|
||||
migrate_skill() {
|
||||
local skill_name="$1"
|
||||
local source_path="$2"
|
||||
local target_dir="$3"
|
||||
local target_path="$target_dir/$skill_name"
|
||||
|
||||
print_info "Migrating: $skill_name"
|
||||
|
||||
# Check for conflicts
|
||||
local resolution
|
||||
resolution=$(resolve_conflict "$skill_name" "$target_dir")
|
||||
|
||||
case "$resolution" in
|
||||
skip)
|
||||
SKIPPED_SKILLS+=("$skill_name (user skipped)")
|
||||
print_warning "Skipped: $skill_name"
|
||||
return 0
|
||||
;;
|
||||
backup)
|
||||
local backup_path
|
||||
backup_path=$(backup_skill "$skill_name" "$target_dir")
|
||||
print_info "Backed up to: $backup_path"
|
||||
;;
|
||||
migrate|overwrite)
|
||||
# Proceed with migration
|
||||
;;
|
||||
esac
|
||||
|
||||
# Perform migration
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
if [[ -d "$target_path" ]]; then
|
||||
rm -rf "$target_path"
|
||||
fi
|
||||
cp -r "$source_path" "$target_path"
|
||||
fi
|
||||
|
||||
MIGRATED_SKILLS+=("$skill_name")
|
||||
print_success "Migrated: $skill_name"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Generate migration report
|
||||
generate_report() {
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " MIGRATION REPORT"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
if [[ ${#MIGRATED_SKILLS[@]} -gt 0 ]]; then
|
||||
print_success "Successfully Migrated (${#MIGRATED_SKILLS[@]}):"
|
||||
for skill in "${MIGRATED_SKILLS[@]}"; do
|
||||
echo " • $skill"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ ${#SKIPPED_SKILLS[@]} -gt 0 ]]; then
|
||||
print_warning "Skipped (${#SKIPPED_SKILLS[@]}):"
|
||||
for skill in "${SKIPPED_SKILLS[@]}"; do
|
||||
echo " • $skill"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ ${#BACKED_UP_SKILLS[@]} -gt 0 ]]; then
|
||||
print_info "Backed Up (${#BACKED_UP_SKILLS[@]}):"
|
||||
for backup in "${BACKED_UP_SKILLS[@]}"; do
|
||||
echo " • $backup"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ ${#ERROR_SKILLS[@]} -gt 0 ]]; then
|
||||
print_error "Failed (${#ERROR_SKILLS[@]}):"
|
||||
for skill in "${ERROR_SKILLS[@]}"; do
|
||||
echo " • $skill"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo "Note: This was a DRY RUN. No changes were made."
|
||||
echo " Run without --dry-run to perform actual migration."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Target Directory: $TARGET_DIR"
|
||||
echo "========================================"
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
# Check for help flag early
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == "-h" ]] || [[ "$arg" == "--help" ]]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "========================================"
|
||||
echo " SKILL MIGRATOR"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# Parse arguments
|
||||
parse_args "$@"
|
||||
|
||||
# Validate source directory
|
||||
if [[ ! -d "$SOURCE_DIR" ]]; then
|
||||
print_error "Source directory does not exist: $SOURCE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check dry run
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
print_warning "DRY RUN MODE - No changes will be made"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Get target directory
|
||||
TARGET_DIR=$(get_target_dir)
|
||||
|
||||
print_info "Source: $SOURCE_DIR"
|
||||
print_info "Target: $TARGET_DIR ($TARGET_MODE)"
|
||||
echo ""
|
||||
|
||||
# Discover skills
|
||||
discover_skills "$SOURCE_DIR"
|
||||
|
||||
# Display discovered skills
|
||||
display_discovered
|
||||
|
||||
# Select skills to migrate
|
||||
SELECTED_INDICES=()
|
||||
select_skills
|
||||
|
||||
# Confirm migration
|
||||
echo ""
|
||||
echo "Ready to migrate ${#SELECTED_INDICES[@]} skill(s)."
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
if [[ "$AUTO_CONFIRM" == true ]]; then
|
||||
print_info "Auto-confirming (--yes flag set)"
|
||||
else
|
||||
read -p "Continue? [Y/n]: " confirm
|
||||
if [[ "$confirm" =~ ^[Nn]$ ]]; then
|
||||
print_info "Migration cancelled"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Migrate selected skills
|
||||
local failed=0
|
||||
for idx in "${SELECTED_INDICES[@]}"; do
|
||||
local array_idx=$((idx - 1))
|
||||
local skill_name="${DISCOVERED_SKILLS[$array_idx]}"
|
||||
local source_path="${DISCOVERED_PATHS[$array_idx]}"
|
||||
|
||||
if ! migrate_skill "$skill_name" "$source_path" "$TARGET_DIR"; then
|
||||
ERROR_SKILLS+=("$skill_name")
|
||||
((failed++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Generate report
|
||||
generate_report
|
||||
|
||||
# Exit with appropriate code
|
||||
if [[ $failed -gt 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user