Reorganize: Move all skills to skills/ folder
- Created skills/ directory - Moved 272 skills to skills/ subfolder - Kept agents/ at root level - Kept installation scripts and docs at root level Repository structure: - skills/ - All 272 skills from skills.sh - agents/ - Agent definitions - *.sh, *.ps1 - Installation scripts - README.md, etc. - Documentation Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
63
skills/plugins/examples/docker-helper/commands/cleanup.ts
Normal file
63
skills/plugins/examples/docker-helper/commands/cleanup.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Docker Cleanup Command
|
||||
* Clean up unused containers, images, and volumes
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export interface CleanupOptions {
|
||||
containers?: boolean
|
||||
images?: boolean
|
||||
volumes?: boolean
|
||||
networks?: boolean
|
||||
all?: boolean
|
||||
}
|
||||
|
||||
export async function handle(args: CleanupOptions, context: any): Promise<string> {
|
||||
const {
|
||||
containers = true,
|
||||
images = true,
|
||||
volumes = false,
|
||||
networks = false,
|
||||
all = false
|
||||
} = args
|
||||
|
||||
const results: string[] = []
|
||||
|
||||
try {
|
||||
if (all || containers) {
|
||||
results.push('Cleaning up stopped containers...')
|
||||
const { stdout: containerOutput } = await execAsync('docker container prune -f')
|
||||
results.push(containerOutput)
|
||||
}
|
||||
|
||||
if (all || images) {
|
||||
results.push('\nCleaning up dangling images...')
|
||||
const { stdout: imageOutput } = await execAsync('docker image prune -a -f')
|
||||
results.push(imageOutput)
|
||||
}
|
||||
|
||||
if (all || volumes) {
|
||||
results.push('\nCleaning up unused volumes...')
|
||||
const { stdout: volumeOutput } = await execAsync('docker volume prune -f')
|
||||
results.push(volumeOutput)
|
||||
}
|
||||
|
||||
if (all || networks) {
|
||||
results.push('\nCleaning up unused networks...')
|
||||
const { stdout: networkOutput } = await execAsync('docker network prune -f')
|
||||
results.push(networkOutput)
|
||||
}
|
||||
|
||||
results.push('\n✓ Docker cleanup complete!')
|
||||
|
||||
return results.join('\n')
|
||||
} catch (error: any) {
|
||||
throw new Error(`Docker cleanup failed: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
74
skills/plugins/examples/docker-helper/commands/deploy.ts
Normal file
74
skills/plugins/examples/docker-helper/commands/deploy.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Docker Deploy Command
|
||||
* Deploy containers with zero-downtime support
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export interface DeployOptions {
|
||||
env?: string
|
||||
noDowntime?: boolean
|
||||
force?: boolean
|
||||
build?: boolean
|
||||
scale?: number
|
||||
}
|
||||
|
||||
export async function handle(args: DeployOptions, context: any): Promise<string> {
|
||||
const { env = 'production', noDowntime = true, force = false, build = true, scale } = args
|
||||
|
||||
try {
|
||||
const results: string[] = []
|
||||
|
||||
// Build if requested
|
||||
if (build) {
|
||||
results.push('Building Docker image...')
|
||||
const { stdout: buildOutput } = await execAsync('docker-compose build')
|
||||
results.push(buildOutput)
|
||||
}
|
||||
|
||||
if (noDowntime && !force) {
|
||||
// Zero-downtime deployment
|
||||
results.push('Starting zero-downtime deployment...')
|
||||
|
||||
// Get current running containers
|
||||
const { stdout: psOutput } = await execAsync('docker-compose ps -q')
|
||||
const hasRunning = psOutput.trim().length > 0
|
||||
|
||||
if (hasRunning) {
|
||||
// Start new containers alongside old ones
|
||||
results.push('Starting new containers...')
|
||||
await execAsync(`docker-compose up -d --scale app=${scale || 2} --no-recreate`)
|
||||
|
||||
// Wait for health checks
|
||||
await new Promise(resolve => setTimeout(resolve, 5000))
|
||||
|
||||
// Stop old containers gracefully
|
||||
results.push('Stopping old containers...')
|
||||
await execAsync('docker-compose up -d --scale app=1 --no-recreate')
|
||||
} else {
|
||||
// First deployment
|
||||
results.push('Initial deployment...')
|
||||
await execAsync('docker-compose up -d')
|
||||
}
|
||||
} else {
|
||||
// Standard deployment with potential downtime
|
||||
results.push('Deploying with potential downtime...')
|
||||
await execAsync('docker-compose up -d --force-recreate')
|
||||
}
|
||||
|
||||
// Show status
|
||||
const { stdout: statusOutput } = await execAsync('docker-compose ps')
|
||||
results.push('\n✓ Deployment complete!\n')
|
||||
results.push(statusOutput)
|
||||
|
||||
return results.join('\n')
|
||||
} catch (error: any) {
|
||||
throw new Error(`Docker deployment failed: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
107
skills/plugins/examples/docker-helper/commands/env.ts
Normal file
107
skills/plugins/examples/docker-helper/commands/env.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Docker Environment Command
|
||||
* Manage environment variables for containers
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export interface EnvOptions {
|
||||
action: 'list' | 'set' | 'unset' | 'export'
|
||||
key?: string
|
||||
value?: string
|
||||
file?: string
|
||||
}
|
||||
|
||||
export async function handle(args: EnvOptions, context: any): Promise<string> {
|
||||
const { action, key, value, file = '.env' } = args
|
||||
|
||||
try {
|
||||
switch (action) {
|
||||
case 'list':
|
||||
return listEnv(file)
|
||||
case 'set':
|
||||
if (!key || value === undefined) {
|
||||
throw new Error('Key and value are required for set action')
|
||||
}
|
||||
return setEnv(file, key, value)
|
||||
case 'unset':
|
||||
if (!key) {
|
||||
throw new Error('Key is required for unset action')
|
||||
}
|
||||
return unsetEnv(file, key)
|
||||
case 'export':
|
||||
return exportEnv(file)
|
||||
default:
|
||||
throw new Error(`Unknown action: ${action}`)
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new Error(`Environment management failed: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
function listEnv(file: string): string {
|
||||
if (!existsSync(file)) {
|
||||
return `# Environment file ${file} does not exist`
|
||||
}
|
||||
|
||||
const content = readFileSync(file, 'utf-8')
|
||||
return content
|
||||
}
|
||||
|
||||
function setEnv(file: string, key: string, value: string): string {
|
||||
let content = ''
|
||||
|
||||
if (existsSync(file)) {
|
||||
content = readFileSync(file, 'utf-8')
|
||||
}
|
||||
|
||||
// Check if key exists
|
||||
const lines = content.split('\n')
|
||||
const keyIndex = lines.findIndex(line => line.startsWith(`${key}=`))
|
||||
|
||||
if (keyIndex >= 0) {
|
||||
// Update existing key
|
||||
lines[keyIndex] = `${key}=${value}`
|
||||
content = lines.join('\n')
|
||||
} else {
|
||||
// Add new key
|
||||
content += `${key}=${value}\n`
|
||||
}
|
||||
|
||||
writeFileSync(file, content, 'utf-8')
|
||||
return `✓ Set ${key} in ${file}`
|
||||
}
|
||||
|
||||
function unsetEnv(file: string, key: string): string {
|
||||
if (!existsSync(file)) {
|
||||
return `# Environment file ${file} does not exist`
|
||||
}
|
||||
|
||||
const content = readFileSync(file, 'utf-8')
|
||||
const lines = content.split('\n')
|
||||
const filtered = lines.filter(line => !line.startsWith(`${key}=`))
|
||||
|
||||
writeFileSync(file, filtered.join('\n'), 'utf-8')
|
||||
return `✓ Unset ${key} from ${file}`
|
||||
}
|
||||
|
||||
function exportEnv(file: string): string {
|
||||
if (!existsSync(file)) {
|
||||
return `# Environment file ${file} does not exist`
|
||||
}
|
||||
|
||||
const content = readFileSync(file, 'utf-8')
|
||||
const exportCommands = content
|
||||
.split('\n')
|
||||
.filter(line => line.trim() && !line.startsWith('#'))
|
||||
.map(line => `export ${line}`)
|
||||
.join('\n')
|
||||
|
||||
return exportCommands
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
58
skills/plugins/examples/docker-helper/commands/logs.ts
Normal file
58
skills/plugins/examples/docker-helper/commands/logs.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Docker Logs Command
|
||||
* View and filter container logs
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export interface LogsOptions {
|
||||
service?: string
|
||||
tail?: number
|
||||
follow?: boolean
|
||||
since?: string
|
||||
grep?: string
|
||||
}
|
||||
|
||||
export async function handle(args: LogsOptions, context: any): Promise<string> {
|
||||
const { service, tail = 100, follow = false, since, grep } = args
|
||||
|
||||
try {
|
||||
let command = 'docker-compose logs'
|
||||
|
||||
if (tail) {
|
||||
command += ` --tail ${tail}`
|
||||
}
|
||||
|
||||
if (follow) {
|
||||
command += ' -f'
|
||||
}
|
||||
|
||||
if (since) {
|
||||
command += ` --since "${since}"`
|
||||
}
|
||||
|
||||
if (service) {
|
||||
command += ` ${service}`
|
||||
}
|
||||
|
||||
const { stdout, stderr } = await execAsync(command)
|
||||
|
||||
let logs = stdout
|
||||
|
||||
// Filter by grep pattern if provided
|
||||
if (grep) {
|
||||
const lines = logs.split('\n')
|
||||
const filtered = lines.filter(line => line.includes(grep))
|
||||
logs = filtered.join('\n')
|
||||
}
|
||||
|
||||
return logs || stderr || 'No logs found'
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to fetch logs: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
Reference in New Issue
Block a user