SuperCharge Claude Code v1.0.0 - Complete Customization Package
Features: - 30+ Custom Skills (cognitive, development, UI/UX, autonomous agents) - RalphLoop autonomous agent integration - Multi-AI consultation (Qwen) - Agent management system with sync capabilities - Custom hooks for session management - MCP servers integration - Plugin marketplace setup - Comprehensive installation script Components: - Skills: always-use-superpowers, ralph, brainstorming, ui-ux-pro-max, etc. - Agents: 100+ agents across engineering, marketing, product, etc. - Hooks: session-start-superpowers, qwen-consult, ralph-auto-trigger - Commands: /brainstorm, /write-plan, /execute-plan - MCP Servers: zai-mcp-server, web-search-prime, web-reader, zread - Binaries: ralphloop wrapper Installation: ./supercharge.sh
This commit is contained in:
48
plugins/examples/docker-helper/.claude-plugin/plugin.json
Normal file
48
plugins/examples/docker-helper/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "docker-helper",
|
||||
"version": "1.0.0",
|
||||
"description": "Docker container management without Docker Desktop",
|
||||
"author": "Your Name",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/yourusername/claude-docker-helper",
|
||||
"claude": {
|
||||
"permissions": [
|
||||
"read:files",
|
||||
"write:files",
|
||||
"execute:commands"
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"name": "docker:deploy",
|
||||
"description": "Deploy containers with zero-downtime support",
|
||||
"handler": "commands/deploy.ts",
|
||||
"permissions": ["execute:commands"]
|
||||
},
|
||||
{
|
||||
"name": "docker:logs",
|
||||
"description": "View and filter container logs",
|
||||
"handler": "commands/logs.ts",
|
||||
"permissions": ["execute:commands"]
|
||||
},
|
||||
{
|
||||
"name": "docker:cleanup",
|
||||
"description": "Clean up unused containers, images, and volumes",
|
||||
"handler": "commands/cleanup.ts",
|
||||
"permissions": ["execute:commands"]
|
||||
},
|
||||
{
|
||||
"name": "docker:env",
|
||||
"description": "Manage environment variables for containers",
|
||||
"handler": "commands/env.ts",
|
||||
"permissions": ["read:files", "write:files"]
|
||||
}
|
||||
],
|
||||
"hooks": [
|
||||
{
|
||||
"event": "SessionStart",
|
||||
"handler": "hooks/check-docker.ts",
|
||||
"priority": 100
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
63
plugins/examples/docker-helper/commands/cleanup.ts
Normal file
63
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
plugins/examples/docker-helper/commands/deploy.ts
Normal file
74
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
plugins/examples/docker-helper/commands/env.ts
Normal file
107
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
plugins/examples/docker-helper/commands/logs.ts
Normal file
58
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 }
|
||||
47
plugins/examples/git-workflow/.claude-plugin/plugin.json
Normal file
47
plugins/examples/git-workflow/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "git-workflow",
|
||||
"version": "1.0.0",
|
||||
"description": "Enhanced Git workflow automation for Claude Code",
|
||||
"author": "Your Name",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/yourusername/claude-git-workflow",
|
||||
"claude": {
|
||||
"permissions": [
|
||||
"read:files",
|
||||
"write:files",
|
||||
"execute:commands"
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"name": "git:smart-commit",
|
||||
"description": "Create an intelligent commit with auto-staging and conventional commits",
|
||||
"handler": "commands/smart-commit.ts",
|
||||
"permissions": ["execute:commands"]
|
||||
},
|
||||
{
|
||||
"name": "git:pr-create",
|
||||
"description": "Create a pull request with AI-generated description",
|
||||
"handler": "commands/pr-create.ts",
|
||||
"permissions": ["execute:commands", "network:request"]
|
||||
},
|
||||
{
|
||||
"name": "git:branch-cleanup",
|
||||
"description": "Clean up merged branches locally and remotely",
|
||||
"handler": "commands/branch-cleanup.ts",
|
||||
"permissions": ["execute:commands"]
|
||||
}
|
||||
],
|
||||
"hooks": [
|
||||
{
|
||||
"event": "PostFileEdit",
|
||||
"handler": "hooks/auto-stage.ts",
|
||||
"priority": 10
|
||||
},
|
||||
{
|
||||
"event": "SessionEnd",
|
||||
"handler": "hooks/save-work.ts",
|
||||
"priority": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
87
plugins/examples/git-workflow/commands/branch-cleanup.ts
Normal file
87
plugins/examples/git-workflow/commands/branch-cleanup.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Branch Cleanup Command
|
||||
* Removes merged branches locally and remotely
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export interface BranchCleanupOptions {
|
||||
local?: boolean
|
||||
remote?: boolean
|
||||
exclude?: string[]
|
||||
dryRun?: boolean
|
||||
}
|
||||
|
||||
export async function handle(
|
||||
args: BranchCleanupOptions,
|
||||
context: any
|
||||
): Promise<string> {
|
||||
const { local = true, remote = true, exclude = ['main', 'master', 'develop'], dryRun = false } = args
|
||||
|
||||
const results: string[] = []
|
||||
|
||||
try {
|
||||
// Get current branch
|
||||
const { stdout: currentBranch } = await execAsync('git rev-parse --abbrev-ref HEAD')
|
||||
|
||||
if (local) {
|
||||
// Get merged local branches
|
||||
const { stdout: mergedBranches } = await execAsync('git branch --merged')
|
||||
|
||||
const branchesToDelete = mergedBranches
|
||||
.split('\n')
|
||||
.map((b) => b.trim().replace('*', '').trim())
|
||||
.filter(
|
||||
(branch) =>
|
||||
branch &&
|
||||
branch !== currentBranch.trim() &&
|
||||
!exclude.includes(branch)
|
||||
)
|
||||
|
||||
if (branchesToDelete.length > 0) {
|
||||
if (dryRun) {
|
||||
results.push(`Would delete local branches: ${branchesToDelete.join(', ')}`)
|
||||
} else {
|
||||
for (const branch of branchesToDelete) {
|
||||
await execAsync(`git branch -d ${branch}`)
|
||||
results.push(`✓ Deleted local branch: ${branch}`)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
results.push('No local branches to clean up')
|
||||
}
|
||||
}
|
||||
|
||||
if (remote) {
|
||||
// Get merged remote branches
|
||||
const { stdout: remoteBranches } = await execAsync('git branch -r --merged')
|
||||
|
||||
const branchesToDelete = remoteBranches
|
||||
.split('\n')
|
||||
.map((b) => b.trim().replace('origin/', '').trim())
|
||||
.filter((branch) => branch && branch !== 'HEAD' && !exclude.includes(branch))
|
||||
|
||||
if (branchesToDelete.length > 0) {
|
||||
if (dryRun) {
|
||||
results.push(`Would delete remote branches: ${branchesToDelete.join(', ')}`)
|
||||
} else {
|
||||
for (const branch of branchesToDelete) {
|
||||
await execAsync(`git push origin --delete ${branch}`)
|
||||
results.push(`✓ Deleted remote branch: ${branch}`)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
results.push('No remote branches to clean up')
|
||||
}
|
||||
}
|
||||
|
||||
return results.join('\n')
|
||||
} catch (error: any) {
|
||||
throw new Error(`Branch cleanup failed: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
60
plugins/examples/git-workflow/commands/pr-create.ts
Normal file
60
plugins/examples/git-workflow/commands/pr-create.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Pull Request Creation Command
|
||||
* Creates a PR with AI-generated description
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export interface PRCreateOptions {
|
||||
title?: string
|
||||
base?: string
|
||||
draft?: boolean
|
||||
reviewers?: string[]
|
||||
}
|
||||
|
||||
export async function handle(args: PRCreateOptions, context: any): Promise<string> {
|
||||
const { title, base = 'main', draft = false, reviewers } = args
|
||||
|
||||
try {
|
||||
// Get current branch
|
||||
const { stdout: branchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD')
|
||||
const branch = branchOutput.trim()
|
||||
|
||||
// Get default title from branch name if not provided
|
||||
const prTitle = title || branchToTitle(branch)
|
||||
|
||||
// Get commits for description
|
||||
const { stdout: commits } = await execAsync(`git log ${base}..HEAD --oneline`)
|
||||
|
||||
// Generate description
|
||||
const description = `## Changes\n\n${commits}\n\n## Summary\n\nAutomated PR created from branch ${branch}`
|
||||
|
||||
// Create PR using gh CLI
|
||||
const draftFlag = draft ? '--draft' : ''
|
||||
const reviewersFlag = reviewers ? `--reviewer ${reviewers.join(',')}` : ''
|
||||
|
||||
const { stdout } = await execAsync(
|
||||
`gh pr create --base ${base} --title "${prTitle}" --body "${description}" ${draftFlag} ${reviewersFlag}`
|
||||
)
|
||||
|
||||
return `✓ Pull request created:\n${stdout}`
|
||||
} catch (error: any) {
|
||||
throw new Error(`PR creation failed: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
function branchToTitle(branch: string): string {
|
||||
return branch
|
||||
.replace(/^(feat|fix|docs|style|refactor|test|chore)\//i, '')
|
||||
.replace(/-/g, ' ')
|
||||
.replace(/_/g, ' ')
|
||||
.split(' ')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
61
plugins/examples/git-workflow/commands/smart-commit.ts
Normal file
61
plugins/examples/git-workflow/commands/smart-commit.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Git Smart Commit Command
|
||||
* Creates intelligent commits with auto-staging and conventional commits
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export interface SmartCommitOptions {
|
||||
message?: string
|
||||
type?: 'feat' | 'fix' | 'docs' | 'style' | 'refactor' | 'test' | 'chore'
|
||||
scope?: string
|
||||
stageAll?: boolean
|
||||
amend?: boolean
|
||||
}
|
||||
|
||||
export async function handle(
|
||||
args: SmartCommitOptions,
|
||||
context: any
|
||||
): Promise<string> {
|
||||
const { message, type = 'feat', scope, stageAll = true, amend = false } = args
|
||||
|
||||
try {
|
||||
// Stage files if requested
|
||||
if (stageAll && !amend) {
|
||||
await execAsync('git add -A')
|
||||
context.sandbox.log('Staged all changes')
|
||||
}
|
||||
|
||||
// Generate commit message if not provided
|
||||
let commitMessage = message
|
||||
|
||||
if (!commitMessage) {
|
||||
// Get diff to generate intelligent message
|
||||
const { stdout: diff } = await execAsync('git diff --cached --stat')
|
||||
|
||||
if (!diff || diff.trim().length === 0) {
|
||||
return 'No changes to commit'
|
||||
}
|
||||
|
||||
// Simple auto-generation based on diff
|
||||
commitMessage = `${type}${scope ? `(${scope})` : ''}: `
|
||||
commitMessage += 'update code based on changes'
|
||||
}
|
||||
|
||||
// Create commit
|
||||
const amendFlag = amend ? '--amend' : ''
|
||||
const { stdout } = await execAsync(
|
||||
`git commit ${amendFlag} -m "${commitMessage}"`
|
||||
)
|
||||
|
||||
return `✓ Committed successfully:\n${stdout}`
|
||||
} catch (error: any) {
|
||||
throw new Error(`Git commit failed: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Export for CLI usage
|
||||
export default { handle }
|
||||
48
plugins/examples/knowledge-base/.claude-plugin/plugin.json
Normal file
48
plugins/examples/knowledge-base/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "knowledge-base",
|
||||
"version": "1.0.0",
|
||||
"description": "AI-powered knowledge base with semantic search",
|
||||
"author": "Your Name",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/yourusername/claude-knowledge-base",
|
||||
"claude": {
|
||||
"permissions": [
|
||||
"read:files",
|
||||
"write:files",
|
||||
"execute:commands"
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"name": "knowledge:add",
|
||||
"description": "Add knowledge entries to your knowledge base",
|
||||
"handler": "commands/add.ts",
|
||||
"permissions": ["write:files"]
|
||||
},
|
||||
{
|
||||
"name": "knowledge:search",
|
||||
"description": "Search your knowledge base with semantic understanding",
|
||||
"handler": "commands/search.ts",
|
||||
"permissions": ["read:files"]
|
||||
},
|
||||
{
|
||||
"name": "knowledge:list",
|
||||
"description": "List all knowledge entries",
|
||||
"handler": "commands/list.ts",
|
||||
"permissions": ["read:files"]
|
||||
},
|
||||
{
|
||||
"name": "knowledge:export",
|
||||
"description": "Export knowledge base to various formats",
|
||||
"handler": "commands/export.ts",
|
||||
"permissions": ["read:files"]
|
||||
}
|
||||
],
|
||||
"hooks": [
|
||||
{
|
||||
"event": "SessionEnd",
|
||||
"handler": "hooks/auto-save.ts",
|
||||
"priority": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
77
plugins/examples/knowledge-base/commands/add.ts
Normal file
77
plugins/examples/knowledge-base/commands/add.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Knowledge Add Command
|
||||
* Add knowledge entries to your knowledge base
|
||||
*/
|
||||
|
||||
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { homedir } from 'os'
|
||||
|
||||
export interface AddOptions {
|
||||
content: string
|
||||
tags?: string[]
|
||||
category?: string
|
||||
title?: string
|
||||
source?: string
|
||||
}
|
||||
|
||||
const KNOWLEDGE_DIR = join(homedir(), '.claude', 'knowledge')
|
||||
const KNOWLEDGE_FILE = join(KNOWLEDGE_DIR, 'knowledge.json')
|
||||
|
||||
interface KnowledgeEntry {
|
||||
id: string
|
||||
title?: string
|
||||
content: string
|
||||
tags: string[]
|
||||
category: string
|
||||
source?: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export async function handle(args: AddOptions, context: any): Promise<string> {
|
||||
const { content, tags = [], category = 'general', title, source } = args
|
||||
|
||||
try {
|
||||
// Ensure knowledge directory exists
|
||||
if (!existsSync(KNOWLEDGE_DIR)) {
|
||||
mkdirSync(KNOWLEDGE_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
// Load existing knowledge
|
||||
let knowledge: KnowledgeEntry[] = []
|
||||
|
||||
if (existsSync(KNOWLEDGE_FILE)) {
|
||||
const data = readFileSync(KNOWLEDGE_FILE, 'utf-8')
|
||||
knowledge = JSON.parse(data)
|
||||
}
|
||||
|
||||
// Create new entry
|
||||
const entry: KnowledgeEntry = {
|
||||
id: generateId(),
|
||||
title,
|
||||
content,
|
||||
tags,
|
||||
category,
|
||||
source,
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
|
||||
knowledge.push(entry)
|
||||
|
||||
// Save knowledge
|
||||
writeFileSync(KNOWLEDGE_FILE, JSON.stringify(knowledge, null, 2), 'utf-8')
|
||||
|
||||
return `✓ Knowledge entry added (ID: ${entry.id})\n` +
|
||||
` Category: ${category}\n` +
|
||||
` Tags: ${tags.join(', ') || 'none'}\n` +
|
||||
` Total entries: ${knowledge.length}`
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to add knowledge: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
function generateId(): string {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substr(2)
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
115
plugins/examples/knowledge-base/commands/export.ts
Normal file
115
plugins/examples/knowledge-base/commands/export.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Knowledge Export Command
|
||||
* Export knowledge base to various formats
|
||||
*/
|
||||
|
||||
import { writeFileSync, readFileSync, existsSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { homedir } from 'os'
|
||||
|
||||
export interface ExportOptions {
|
||||
format: 'json' | 'markdown' | 'csv'
|
||||
outputPath?: string
|
||||
category?: string
|
||||
}
|
||||
|
||||
const KNOWLEDGE_FILE = join(homedir(), '.claude', 'knowledge', 'knowledge.json')
|
||||
|
||||
interface KnowledgeEntry {
|
||||
id: string
|
||||
title?: string
|
||||
content: string
|
||||
tags: string[]
|
||||
category: string
|
||||
source?: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export async function handle(args: ExportOptions, context: any): Promise<string> {
|
||||
const { format, outputPath, category } = args
|
||||
|
||||
try {
|
||||
if (!existsSync(KNOWLEDGE_FILE)) {
|
||||
return 'Knowledge base is empty. Add some knowledge first!'
|
||||
}
|
||||
|
||||
const data = readFileSync(KNOWLEDGE_FILE, 'utf-8')
|
||||
let knowledge: KnowledgeEntry[] = JSON.parse(data)
|
||||
|
||||
if (category) {
|
||||
knowledge = knowledge.filter(entry => entry.category === category)
|
||||
}
|
||||
|
||||
let content: string
|
||||
let defaultPath: string
|
||||
|
||||
switch (format) {
|
||||
case 'json':
|
||||
content = JSON.stringify(knowledge, null, 2)
|
||||
defaultPath = join(homedir(), 'knowledge-export.json')
|
||||
break
|
||||
|
||||
case 'markdown':
|
||||
content = exportAsMarkdown(knowledge)
|
||||
defaultPath = join(homedir(), 'knowledge-export.md')
|
||||
break
|
||||
|
||||
case 'csv':
|
||||
content = exportAsCSV(knowledge)
|
||||
defaultPath = join(homedir(), 'knowledge-export.csv')
|
||||
break
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown format: ${format}`)
|
||||
}
|
||||
|
||||
const output = outputPath || defaultPath
|
||||
writeFileSync(output, content, 'utf-8')
|
||||
|
||||
return `✓ Exported ${knowledge.length} entries to ${output}`
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to export knowledge: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
function exportAsMarkdown(entries: KnowledgeEntry[]): string {
|
||||
const lines: string[] = ['# Knowledge Base Export', '', `Generated: ${new Date().toISOString()}`, '']
|
||||
|
||||
for (const entry of entries) {
|
||||
lines.push(`## ${entry.title || 'Untitled'}`)
|
||||
lines.push(`**ID:** ${entry.id}`)
|
||||
lines.push(`**Category:** ${entry.category}`)
|
||||
lines.push(`**Tags:** ${entry.tags.join(', ') || 'none'}`)
|
||||
lines.push(`**Date:** ${new Date(entry.timestamp).toLocaleString()}`)
|
||||
if (entry.source) {
|
||||
lines.push(`**Source:** ${entry.source}`)
|
||||
}
|
||||
lines.push('')
|
||||
lines.push(entry.content)
|
||||
lines.push('')
|
||||
lines.push('---')
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
function exportAsCSV(entries: KnowledgeEntry[]): string {
|
||||
const headers = ['ID', 'Title', 'Category', 'Tags', 'Content', 'Source', 'Date']
|
||||
const rows = entries.map(entry => [
|
||||
entry.id,
|
||||
entry.title || '',
|
||||
entry.category,
|
||||
entry.tags.join('; '),
|
||||
`"${entry.content.replace(/"/g, '""')}"`,
|
||||
entry.source || '',
|
||||
entry.timestamp
|
||||
])
|
||||
|
||||
return [
|
||||
headers.join(','),
|
||||
...rows.map(row => row.join(','))
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
74
plugins/examples/knowledge-base/commands/list.ts
Normal file
74
plugins/examples/knowledge-base/commands/list.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Knowledge List Command
|
||||
* List all knowledge entries
|
||||
*/
|
||||
|
||||
import { readFileSync, existsSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { homedir } from 'os'
|
||||
|
||||
export interface ListOptions {
|
||||
category?: string
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
const KNOWLEDGE_FILE = join(homedir(), '.claude', 'knowledge', 'knowledge.json')
|
||||
|
||||
interface KnowledgeEntry {
|
||||
id: string
|
||||
title?: string
|
||||
content: string
|
||||
tags: string[]
|
||||
category: string
|
||||
source?: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export async function handle(args: ListOptions, context: any): Promise<string> {
|
||||
const { category, tags } = args
|
||||
|
||||
try {
|
||||
if (!existsSync(KNOWLEDGE_FILE)) {
|
||||
return 'Knowledge base is empty. Add some knowledge first!'
|
||||
}
|
||||
|
||||
const data = readFileSync(KNOWLEDGE_FILE, 'utf-8')
|
||||
const knowledge: KnowledgeEntry[] = JSON.parse(data)
|
||||
|
||||
// Group by category
|
||||
const byCategory: Record<string, KnowledgeEntry[]> = {}
|
||||
|
||||
for (const entry of knowledge) {
|
||||
if (category && entry.category !== category) continue
|
||||
if (tags && !tags.some(t => entry.tags.includes(t))) continue
|
||||
|
||||
if (!byCategory[entry.category]) {
|
||||
byCategory[entry.category] = []
|
||||
}
|
||||
byCategory[entry.category].push(entry)
|
||||
}
|
||||
|
||||
if (Object.keys(byCategory).length === 0) {
|
||||
return 'No entries found matching criteria'
|
||||
}
|
||||
|
||||
// Format output
|
||||
const lines: string[] = []
|
||||
|
||||
for (const [cat, entries] of Object.entries(byCategory)) {
|
||||
lines.push(`\n📁 ${cat} (${entries.length} entries)`)
|
||||
|
||||
for (const entry of entries) {
|
||||
const title = entry.title || entry.content.slice(0, 50)
|
||||
const date = new Date(entry.timestamp).toLocaleDateString()
|
||||
lines.push(` • ${title} [${entry.tags.join(', ') || 'no tags'}] - ${date}`)
|
||||
}
|
||||
}
|
||||
|
||||
return `\n📚 Knowledge Base (${knowledge.length} total entries)\n${lines.join('\n')}`
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to list knowledge: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
91
plugins/examples/knowledge-base/commands/search.ts
Normal file
91
plugins/examples/knowledge-base/commands/search.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Knowledge Search Command
|
||||
* Search your knowledge base with semantic understanding
|
||||
*/
|
||||
|
||||
import { readFileSync, existsSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { homedir } from 'os'
|
||||
|
||||
export interface SearchOptions {
|
||||
query: string
|
||||
category?: string
|
||||
tags?: string[]
|
||||
limit?: number
|
||||
}
|
||||
|
||||
const KNOWLEDGE_FILE = join(homedir(), '.claude', 'knowledge', 'knowledge.json')
|
||||
|
||||
interface KnowledgeEntry {
|
||||
id: string
|
||||
title?: string
|
||||
content: string
|
||||
tags: string[]
|
||||
category: string
|
||||
source?: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export async function handle(args: SearchOptions, context: any): Promise<string> {
|
||||
const { query, category, tags, limit = 10 } = args
|
||||
|
||||
try {
|
||||
if (!existsSync(KNOWLEDGE_FILE)) {
|
||||
return 'Knowledge base is empty. Add some knowledge first!'
|
||||
}
|
||||
|
||||
const data = readFileSync(KNOWLEDGE_FILE, 'utf-8')
|
||||
const knowledge: KnowledgeEntry[] = JSON.parse(data)
|
||||
|
||||
// Filter knowledge
|
||||
let results = knowledge
|
||||
|
||||
if (category) {
|
||||
results = results.filter(entry => entry.category === category)
|
||||
}
|
||||
|
||||
if (tags && tags.length > 0) {
|
||||
results = results.filter(entry =>
|
||||
tags.some(tag => entry.tags.includes(tag))
|
||||
)
|
||||
}
|
||||
|
||||
// Text search (simple implementation)
|
||||
if (query) {
|
||||
const queryLower = query.toLowerCase()
|
||||
results = results.filter(entry =>
|
||||
entry.content.toLowerCase().includes(queryLower) ||
|
||||
(entry.title && entry.title.toLowerCase().includes(queryLower)) ||
|
||||
entry.tags.some(tag => tag.toLowerCase().includes(queryLower))
|
||||
)
|
||||
}
|
||||
|
||||
// Limit results
|
||||
results = results.slice(0, limit)
|
||||
|
||||
if (results.length === 0) {
|
||||
return `No results found for query: "${query}"`
|
||||
}
|
||||
|
||||
// Format results
|
||||
const formatted = results.map(entry => {
|
||||
const lines = [
|
||||
`ID: ${entry.id}`,
|
||||
entry.title ? `Title: ${entry.title}` : null,
|
||||
`Category: ${entry.category}`,
|
||||
`Tags: ${entry.tags.join(', ') || 'none'}`,
|
||||
`Date: ${new Date(entry.timestamp).toLocaleDateString()}`,
|
||||
'',
|
||||
entry.content.slice(0, 200) + (entry.content.length > 200 ? '...' : ''),
|
||||
''
|
||||
]
|
||||
return lines.filter(Boolean).join('\n')
|
||||
})
|
||||
|
||||
return `Found ${results.length} result(s):\n\n${formatted.join('\n---\n\n')}`
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to search knowledge: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default { handle }
|
||||
Reference in New Issue
Block a user