vibes-plug 2.14.1 ā 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/vibes-plug-core.md +5 -0
- package/.cursor/rules/vibes-plug-core.mdc +7 -2
- package/.cursorrules +8 -2
- package/AGENTS.md +23 -2
- package/CHANGELOG.md +114 -0
- package/CLAUDE.md +10 -3
- package/README.md +216 -611
- package/bin/vibes.mjs +1104 -0
- package/package.json +11 -3
- package/plugin.json +4 -3
- package/scripts/check-anti-slop.mjs +53 -0
- package/scripts/install.js +3 -1
- package/scripts/update_skills.js +1 -1
- package/scripts/update_skills.mjs +86 -0
- package/scripts/validate-skills.mjs +111 -0
- package/skills/accessibility-testing-expert/SKILL.md +117 -116
- package/skills/affective-computing-emotion-ai/SKILL.md +83 -0
- package/skills/agentic-coding-workflow-expert/SKILL.md +297 -0
- package/skills/agentic-memory-architect/SKILL.md +52 -0
- package/skills/agentic-micro-economy-architect/SKILL.md +92 -0
- package/skills/ai-llm-integration-expert/SKILL.md +330 -194
- package/skills/ai-media-generation-expert/SKILL.md +173 -172
- package/skills/ai-prompt-engineering-expert/SKILL.md +204 -134
- package/skills/ai-safety-governance-expert/SKILL.md +223 -0
- package/skills/angular-expert/SKILL.md +149 -148
- package/skills/anti-slop/SKILL.md +134 -133
- package/skills/api-design-expert/SKILL.md +4 -3
- package/skills/api-gateway-proxy-expert/SKILL.md +3 -2
- package/skills/app-analyzer-optimizer/SKILL.md +4 -3
- package/skills/apple-ecosystem-expert/SKILL.md +6 -5
- package/skills/astro-framework-expert/SKILL.md +201 -200
- package/skills/async-queue-temporal-expert/SKILL.md +218 -217
- package/skills/authentication-identity-expert/SKILL.md +174 -173
- package/skills/autonomous-red-teamer/SKILL.md +338 -203
- package/skills/autonomous-tdd-debugger/SKILL.md +6 -5
- package/skills/biome-linter-formatter-expert/SKILL.md +90 -89
- package/skills/blockchain-web3-expert/SKILL.md +116 -115
- package/skills/brainstorming/SKILL.md +392 -377
- package/skills/browser-automation-expert/SKILL.md +260 -222
- package/skills/bun-runtime-expert/SKILL.md +5 -4
- package/skills/chatbot-messaging-expert/SKILL.md +115 -114
- package/skills/ci-cd-devops-architect/SKILL.md +3 -2
- package/skills/cloud-hosting-expert/SKILL.md +5 -4
- package/skills/coderabbit/SKILL.md +5 -4
- package/skills/compliance-gdpr-privacy-expert/SKILL.md +3 -2
- package/skills/composable-mach-architect/SKILL.md +338 -0
- package/skills/cron-scheduler-expert/SKILL.md +5 -4
- package/skills/data-pipeline-etl-expert/SKILL.md +3 -2
- package/skills/data-telemetry-expert/SKILL.md +5 -4
- package/skills/data-visualization-expert/SKILL.md +155 -154
- package/skills/database-orm-expert/SKILL.md +166 -165
- package/skills/deep-research-analyst/SKILL.md +182 -136
- package/skills/dependency-upgrade-migrator/SKILL.md +11 -10
- package/skills/design-system-architect/SKILL.md +4 -3
- package/skills/desktop-electron-expert/SKILL.md +129 -128
- package/skills/documentation-site-expert/SKILL.md +60 -59
- package/skills/doku-mcp-server/SKILL.md +5 -4
- package/skills/doku-payment-gateway/SKILL.md +250 -232
- package/skills/domain-driven-design-expert/SKILL.md +3 -2
- package/skills/e2e-testing-expert/SKILL.md +5 -4
- package/skills/ecommerce-expert/SKILL.md +88 -87
- package/skills/email-notification-expert/SKILL.md +5 -4
- package/skills/ephemeral-generative-ui-architect/SKILL.md +88 -0
- package/skills/error-resilience-expert/SKILL.md +14 -13
- package/skills/event-driven-architect/SKILL.md +5 -4
- package/skills/feature-flag-analytics-expert/SKILL.md +3 -2
- package/skills/file-upload-media-expert/SKILL.md +5 -4
- package/skills/firebase-security-expert/SKILL.md +5 -4
- package/skills/form-validation-expert/SKILL.md +7 -6
- package/skills/frontier-ai-models-expert/SKILL.md +116 -0
- package/skills/fullstack-expert/SKILL.md +185 -184
- package/skills/gemini-agent-booster/SKILL.md +248 -172
- package/skills/geospatial-maps-expert/SKILL.md +81 -80
- package/skills/global-a11y-i18n-expert/SKILL.md +5 -4
- package/skills/glsl-shader-expert/SKILL.md +191 -190
- package/skills/go-programming-expert/SKILL.md +5 -4
- package/skills/graph-rag-knowledge-expert/SKILL.md +201 -200
- package/skills/graphql-apollo-expert/SKILL.md +5 -4
- package/skills/headless-cms-expert/SKILL.md +182 -181
- package/skills/hig/SKILL.md +5 -4
- package/skills/js-backend-expert/SKILL.md +219 -218
- package/skills/legacy-code-translator/SKILL.md +6 -5
- package/skills/llm-finops-router/SKILL.md +52 -0
- package/skills/local-slm-edge-ai-expert/SKILL.md +168 -167
- package/skills/logging-error-tracking-expert/SKILL.md +5 -4
- package/skills/mcp-server-architect/SKILL.md +315 -307
- package/skills/micro-frontend-architect/SKILL.md +5 -4
- package/skills/mobile-expo-expert/SKILL.md +5 -4
- package/skills/modern-css-native-expert/SKILL.md +190 -189
- package/skills/monorepo-architect/SKILL.md +5 -4
- package/skills/mpa-orchestrator/SKILL.md +41 -4
- package/skills/multi-agent-orchestration/SKILL.md +388 -254
- package/skills/mvc-expert/SKILL.md +5 -4
- package/skills/n8n-automation-expert/SKILL.md +90 -89
- package/skills/nextjs-app-router-expert/SKILL.md +3 -2
- package/skills/openapi-swagger-codegen-expert/SKILL.md +4 -3
- package/skills/payment-gateway-expert/SKILL.md +131 -128
- package/skills/pdf-document-generation-expert/SKILL.md +92 -91
- package/skills/performance-web-vitals/SKILL.md +5 -4
- package/skills/post-quantum-crypto-migrator/SKILL.md +3 -2
- package/skills/prd-architect/SKILL.md +183 -182
- package/skills/proactive-background-watcher/SKILL.md +5 -4
- package/skills/production-ready-hardener/SKILL.md +10 -9
- package/skills/pwa-offline-first-expert/SKILL.md +227 -226
- package/skills/pydantic-ai-expert/SKILL.md +162 -161
- package/skills/python-programming-expert/SKILL.md +5 -4
- package/skills/rate-limit-abuse-prevention/SKILL.md +5 -4
- package/skills/realtime-collaboration-expert/SKILL.md +3 -2
- package/skills/rich-text-editor-expert/SKILL.md +178 -177
- package/skills/rust-programming-expert/SKILL.md +5 -4
- package/skills/saas-architect/SKILL.md +155 -154
- package/skills/saas-billing/SKILL.md +394 -382
- package/skills/saas-multi-tenant/SKILL.md +7 -6
- package/skills/scalability-clean-code/SKILL.md +5 -4
- package/skills/search-engine-expert/SKILL.md +90 -89
- package/skills/self-healing-cloud-orchestrator/SKILL.md +3 -2
- package/skills/senior-frontend/SKILL.md +14 -9
- package/skills/seo/SKILL.md +4 -4
- package/skills/session-memory-manager/SKILL.md +129 -128
- package/skills/solidjs-expert/SKILL.md +81 -80
- package/skills/spa-orchestrator/SKILL.md +5 -4
- package/skills/sse-websocket-streaming-expert/SKILL.md +3 -2
- package/skills/state-management-expert/SKILL.md +5 -4
- package/skills/supabase-security-expert/SKILL.md +5 -4
- package/skills/svelte-sveltekit-expert/SKILL.md +92 -91
- package/skills/svg-animation-motion-expert/SKILL.md +3 -2
- package/skills/synthetic-data-finetuning-expert/SKILL.md +156 -155
- package/skills/tailwind-expert/SKILL.md +62 -5
- package/skills/tanstack-query-expert/SKILL.md +5 -4
- package/skills/tauri-expert/SKILL.md +5 -4
- package/skills/typescript-expert/SKILL.md +5 -4
- package/skills/ui-ux-pro-max/SKILL.md +7 -6
- package/skills/vector-db-rag-expert/SKILL.md +209 -208
- package/skills/vercel-ai-sdk-expert/SKILL.md +226 -181
- package/skills/voice-ai-realtime-agent/SKILL.md +243 -242
- package/skills/vue-frontend-expert/SKILL.md +5 -4
- package/skills/wasm-edge-computing-expert/SKILL.md +3 -2
- package/skills/web-3d-graphics-expert/SKILL.md +314 -313
- package/skills/web-game-engine-expert/SKILL.md +330 -329
- package/skills/web-scraper/SKILL.md +158 -157
- package/skills/website-design-cloner/SKILL.md +5 -4
- package/skills/webxr-ar-vr-expert/SKILL.md +163 -162
- package/skills/wordpress-headless-expert/SKILL.md +145 -144
- package/skills/zero-tech-debt-auditor/SKILL.md +115 -0
- package/skills/zero-to-prod-orchestrator/SKILL.md +281 -229
- package/skills/zero-trust-secret-vault/SKILL.md +3 -2
- package/BLUEPRINT.md +0 -319
- package/skills/bootstrap-to-modern/SKILL.md +0 -94
- package/skills/multiple-entry-points/SKILL.md +0 -91
- package/skills/secure-fuzz-testing/SKILL.md +0 -207
- package/skills/visual-qa-vision-agent/SKILL.md +0 -71
package/bin/vibes.mjs
ADDED
|
@@ -0,0 +1,1104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import readline from 'readline';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const PLUGIN_ROOT = path.join(__dirname, '..');
|
|
11
|
+
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const command = args[0];
|
|
14
|
+
|
|
15
|
+
const rl = readline.createInterface({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const question = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
21
|
+
|
|
22
|
+
async function runInit(projectName) {
|
|
23
|
+
if (!projectName) {
|
|
24
|
+
console.error('ā Error: Project name is required.');
|
|
25
|
+
console.log('Usage: vibes init <project-name>');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const targetDir = path.join(process.cwd(), projectName);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await fs.mkdir(targetDir);
|
|
33
|
+
console.log(`\nš Created project directory: ${projectName}`);
|
|
34
|
+
|
|
35
|
+
// Create standard Zero-To-Prod boilerplate
|
|
36
|
+
const filesToCreate = {
|
|
37
|
+
'README.md': `# ${projectName}\n\nProject initialized with Vibes-Plug Swarm Orchestrator.\n\n## Next Steps\nAsk your AI agent to begin **Phase 1: Discovery** using the \`zero-to-prod-orchestrator\` skill.`,
|
|
38
|
+
'PROGRESS.md': `# š Zero to Prod - Progress Tracker\n\n- [ ] Phase 1: Discovery & PRD\n- [ ] Phase 2: Foundation & Monorepo\n- [ ] Phase 3: Database & Auth\n- [ ] Phase 4: Backend & APIs\n- [ ] Phase 5: Frontend & UI\n- [ ] Phase 6: Testing & Security\n- [ ] Phase 7: DevOps & Monitoring\n- [ ] Phase 8: Launch`,
|
|
39
|
+
'BLUEPRINT.md': `# š System Blueprint\n\n(Auto-generated architectural decisions will be logged here by the AI agent)`,
|
|
40
|
+
'PRD.md': `# š Product Requirements Document\n\n(To be filled by the deep-research-analyst agent)`
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for (const [filename, content] of Object.entries(filesToCreate)) {
|
|
44
|
+
await fs.writeFile(path.join(targetDir, filename), content, 'utf8');
|
|
45
|
+
console.log(` š Created ${filename}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log('\nš Project scaffolded successfully!');
|
|
49
|
+
console.log('\nTo get started:');
|
|
50
|
+
console.log(` cd ${projectName}`);
|
|
51
|
+
console.log(' Ask your AI (Claude/Cursor/Antigravity): "Begin phase 1 using zero-to-prod-orchestrator"');
|
|
52
|
+
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.error(`\nā Error creating project: ${err.message}`);
|
|
55
|
+
} finally {
|
|
56
|
+
rl.close();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function runBootstrap(templateName, projectName) {
|
|
61
|
+
if (!templateName || !projectName) {
|
|
62
|
+
console.error('ā Error: Template name and Project name are required.');
|
|
63
|
+
console.log('Usage: vibes bootstrap <template> <project-name>');
|
|
64
|
+
console.log('Available templates: saas, ecommerce');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const validTemplates = ['saas', 'ecommerce', 'mobile', 'api', 'fullstack'];
|
|
69
|
+
if (!validTemplates.includes(templateName)) {
|
|
70
|
+
console.error(`ā Error: Invalid template '${templateName}'. Valid options: ${validTemplates.join(', ')}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const targetDir = path.join(process.cwd(), projectName);
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const exists = await fs.access(targetDir).then(() => true).catch(() => false);
|
|
78
|
+
if (exists) {
|
|
79
|
+
console.error(`ā Error: Directory '${projectName}' already exists.`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Resolve template-specific config first
|
|
84
|
+
let scaffoldCmd = null;
|
|
85
|
+
let prdTitle = projectName;
|
|
86
|
+
let specificSkills = [];
|
|
87
|
+
|
|
88
|
+
if (templateName === 'saas') {
|
|
89
|
+
prdTitle = 'Multi-Tenant SaaS';
|
|
90
|
+
scaffoldCmd = `npx create-next-app@latest ${projectName} --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm --yes`;
|
|
91
|
+
specificSkills = ['saas-architect', 'saas-multi-tenant', 'saas-billing', 'payment-gateway-expert', 'supabase-security-expert'];
|
|
92
|
+
} else if (templateName === 'ecommerce') {
|
|
93
|
+
prdTitle = 'E-Commerce Platform';
|
|
94
|
+
scaffoldCmd = `npx create-next-app@latest ${projectName} --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm --yes`;
|
|
95
|
+
specificSkills = ['ecommerce-expert', 'payment-gateway-expert', 'database-orm-expert', 'doku-payment-gateway'];
|
|
96
|
+
} else if (templateName === 'mobile') {
|
|
97
|
+
prdTitle = 'Mobile App (Expo)';
|
|
98
|
+
scaffoldCmd = `npx create-expo-app@latest ${projectName} --template blank-typescript --yes`;
|
|
99
|
+
specificSkills = ['mobile-expo-expert', 'authentication-identity-expert', 'api-design-expert', 'state-management-expert'];
|
|
100
|
+
} else if (templateName === 'api') {
|
|
101
|
+
prdTitle = 'Backend API (Hono/Node.js)';
|
|
102
|
+
scaffoldCmd = `npx create-hono@latest ${projectName} --template nodejs --pm npm --install`;
|
|
103
|
+
specificSkills = ['js-backend-expert', 'api-design-expert', 'database-orm-expert', 'authentication-identity-expert', 'logging-error-tracking-expert'];
|
|
104
|
+
} else if (templateName === 'fullstack') {
|
|
105
|
+
prdTitle = 'Full-Stack T3 App';
|
|
106
|
+
scaffoldCmd = `npx create-t3-app@latest ${projectName} --CI --noGit --appRouter --tailwind --trpc --prisma --nextAuth`;
|
|
107
|
+
specificSkills = ['nextjs-app-router-expert', 'saas-architect', 'database-orm-expert', 'authentication-identity-expert', 'tailwind-expert'];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log(`\n\ud83d\ude80 Bootstrapping ${prdTitle} project: ${projectName}...`);
|
|
111
|
+
console.log('\u23f3 This may take a minute or two as npm installs dependencies...\n');
|
|
112
|
+
|
|
113
|
+
const { execSync } = await import('child_process');
|
|
114
|
+
execSync(scaffoldCmd, { stdio: 'inherit' });
|
|
115
|
+
|
|
116
|
+
console.log(`\nš Project scaffolded. Injecting AI skills for ${templateName}...`);
|
|
117
|
+
|
|
118
|
+
// Core skills for all templates
|
|
119
|
+
const coreSkills = [
|
|
120
|
+
'zero-to-prod-orchestrator',
|
|
121
|
+
'senior-frontend',
|
|
122
|
+
'tailwind-expert',
|
|
123
|
+
'anti-slop',
|
|
124
|
+
'session-memory-manager'
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
const allSkills = [...coreSkills, ...specificSkills];
|
|
128
|
+
const targetAgentsDir = path.join(targetDir, '.agents', 'skills');
|
|
129
|
+
await fs.mkdir(targetAgentsDir, { recursive: true });
|
|
130
|
+
|
|
131
|
+
for (const skill of allSkills) {
|
|
132
|
+
const sourceDir = path.join(PLUGIN_ROOT, 'skills', skill);
|
|
133
|
+
const skillTargetDir = path.join(targetAgentsDir, skill);
|
|
134
|
+
|
|
135
|
+
const skillExists = await fs.access(sourceDir).then(() => true).catch(() => false);
|
|
136
|
+
if (skillExists) {
|
|
137
|
+
await fs.cp(sourceDir, skillTargetDir, { recursive: true });
|
|
138
|
+
console.log(` ā Injected skill: ${skill}`);
|
|
139
|
+
} else {
|
|
140
|
+
console.warn(` ā ļø Warning: Skill '${skill}' not found in global registry.`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Overwrite README and PRD
|
|
145
|
+
const filesToCreate = {
|
|
146
|
+
'README.md': `# ${projectName}\n\nProject initialized with Vibes-Plug Swarm Orchestrator (${templateName.toUpperCase()} template).\n\n## Next Steps\nAsk your AI agent to begin **Phase 1: Discovery** using the \`zero-to-prod-orchestrator\` skill.`,
|
|
147
|
+
'PRD.md': `# š ${prdTitle} - Product Requirements Document\n\n(To be filled by the deep-research-analyst agent based on the ${templateName} template)`
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
for (const [filename, content] of Object.entries(filesToCreate)) {
|
|
151
|
+
await fs.writeFile(path.join(targetDir, filename), content, 'utf8');
|
|
152
|
+
console.log(` š Created ${filename}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(`\nš Super-Scaffolding complete for ${projectName}!`);
|
|
156
|
+
console.log('\nTo get started:');
|
|
157
|
+
console.log(` cd ${projectName}`);
|
|
158
|
+
console.log(' Ask your AI (Claude/Cursor/Antigravity): "Begin phase 1 using zero-to-prod-orchestrator"');
|
|
159
|
+
|
|
160
|
+
} catch (err) {
|
|
161
|
+
console.error(`\nā Error during bootstrap: ${err.message}`);
|
|
162
|
+
} finally {
|
|
163
|
+
rl.close();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function runValidate() {
|
|
168
|
+
console.log('Running ecosystem validation...\n');
|
|
169
|
+
try {
|
|
170
|
+
const { execSync } = await import('child_process');
|
|
171
|
+
execSync('node ' + path.join(PLUGIN_ROOT, 'scripts', 'validate-skills.mjs'), { stdio: 'inherit' });
|
|
172
|
+
} catch (err) {
|
|
173
|
+
// Error is already printed by the child process
|
|
174
|
+
}
|
|
175
|
+
rl.close();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function runAudit() {
|
|
179
|
+
console.log('Running Anti-AI Slop Audit...\n');
|
|
180
|
+
try {
|
|
181
|
+
const { execSync } = await import('child_process');
|
|
182
|
+
execSync('node ' + path.join(PLUGIN_ROOT, 'scripts', 'check-anti-slop.mjs'), { stdio: 'inherit' });
|
|
183
|
+
} catch (err) {
|
|
184
|
+
// Error is already printed by the child process
|
|
185
|
+
}
|
|
186
|
+
rl.close();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function runCreateSkill(skillName) {
|
|
190
|
+
if (!skillName) {
|
|
191
|
+
console.error('ā Error: Skill name is required.');
|
|
192
|
+
console.log('Usage: vibes create-skill <skill-name>');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const kebabCaseRegex = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
197
|
+
if (!kebabCaseRegex.test(skillName)) {
|
|
198
|
+
console.error('ā Error: Skill name must be in kebab-case (e.g., my-new-skill).');
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const targetDir = path.join(PLUGIN_ROOT, 'skills', skillName);
|
|
203
|
+
const templatePath = path.join(PLUGIN_ROOT, 'templates', 'SKILL_TEMPLATE.md');
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const exists = await fs.access(targetDir).then(() => true).catch(() => false);
|
|
207
|
+
if (exists) {
|
|
208
|
+
console.error(`ā Error: Skill directory '${skillName}' already exists.`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
213
|
+
|
|
214
|
+
let templateContent = await fs.readFile(templatePath, 'utf8');
|
|
215
|
+
|
|
216
|
+
// Basic string replacements
|
|
217
|
+
templateContent = templateContent.replace(/skill-baru/g, skillName);
|
|
218
|
+
templateContent = templateContent.replace(/\[Skill Name\]/g, skillName);
|
|
219
|
+
|
|
220
|
+
await fs.writeFile(path.join(targetDir, 'SKILL.md'), templateContent, 'utf8');
|
|
221
|
+
|
|
222
|
+
console.log(`\nš Scaffolded new skill: ${skillName}`);
|
|
223
|
+
console.log(`š Location: skills/${skillName}/SKILL.md`);
|
|
224
|
+
console.log('\nNext steps:');
|
|
225
|
+
console.log(' 1. Open the file and follow the checklist at the bottom.');
|
|
226
|
+
console.log(' 2. Replace all [...] placeholders with actual content.');
|
|
227
|
+
console.log(' 3. Register your new skill in brainstorming/SKILL.md');
|
|
228
|
+
|
|
229
|
+
} catch (err) {
|
|
230
|
+
console.error(`\nā Error creating skill: ${err.message}`);
|
|
231
|
+
} finally {
|
|
232
|
+
rl.close();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function runCreateMcp(serverName) {
|
|
237
|
+
if (!serverName) {
|
|
238
|
+
console.error('ā Error: MCP Server name is required.');
|
|
239
|
+
console.log('Usage: vibes create-mcp <server-name>');
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const targetDir = path.join(process.cwd(), serverName);
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const exists = await fs.access(targetDir).then(() => true).catch(() => false);
|
|
247
|
+
if (exists) {
|
|
248
|
+
console.error(`ā Error: Directory '${serverName}' already exists.`);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
253
|
+
await fs.mkdir(path.join(targetDir, 'src'), { recursive: true });
|
|
254
|
+
|
|
255
|
+
const filesToCreate = {
|
|
256
|
+
'package.json': `{
|
|
257
|
+
"name": "${serverName}",
|
|
258
|
+
"version": "1.0.0",
|
|
259
|
+
"description": "MCP Server generated by vibes-plug",
|
|
260
|
+
"type": "module",
|
|
261
|
+
"bin": {
|
|
262
|
+
"${serverName}": "./build/index.js"
|
|
263
|
+
},
|
|
264
|
+
"scripts": {
|
|
265
|
+
"build": "tsc",
|
|
266
|
+
"start": "node build/index.js"
|
|
267
|
+
},
|
|
268
|
+
"dependencies": {
|
|
269
|
+
"@modelcontextprotocol/sdk": "latest",
|
|
270
|
+
"zod": "^3.23.8"
|
|
271
|
+
},
|
|
272
|
+
"devDependencies": {
|
|
273
|
+
"@types/node": "^22.0.0",
|
|
274
|
+
"typescript": "^5.5.4"
|
|
275
|
+
}
|
|
276
|
+
}`,
|
|
277
|
+
'tsconfig.json': `{
|
|
278
|
+
"compilerOptions": {
|
|
279
|
+
"target": "ES2022",
|
|
280
|
+
"module": "NodeNext",
|
|
281
|
+
"moduleResolution": "NodeNext",
|
|
282
|
+
"outDir": "./build",
|
|
283
|
+
"rootDir": "./src",
|
|
284
|
+
"strict": true,
|
|
285
|
+
"esModuleInterop": true,
|
|
286
|
+
"skipLibCheck": true,
|
|
287
|
+
"forceConsistentCasingInFileNames": true
|
|
288
|
+
},
|
|
289
|
+
"include": ["src/**/*"]
|
|
290
|
+
}`,
|
|
291
|
+
'src/index.ts': `#!/usr/bin/env node
|
|
292
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
293
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
294
|
+
import {
|
|
295
|
+
CallToolRequestSchema,
|
|
296
|
+
ErrorCode,
|
|
297
|
+
ListToolsRequestSchema,
|
|
298
|
+
McpError,
|
|
299
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
300
|
+
|
|
301
|
+
class ${serverName.replace(/[^a-zA-Z0-9]/g, '')}Server {
|
|
302
|
+
private server: Server;
|
|
303
|
+
|
|
304
|
+
constructor() {
|
|
305
|
+
this.server = new Server(
|
|
306
|
+
{
|
|
307
|
+
name: "${serverName}",
|
|
308
|
+
version: "1.0.0",
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
capabilities: {
|
|
312
|
+
tools: {},
|
|
313
|
+
},
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
this.setupToolHandlers();
|
|
318
|
+
|
|
319
|
+
// Error handling
|
|
320
|
+
this.server.onerror = (error) => console.error('[MCP Error]', error);
|
|
321
|
+
process.on('SIGINT', async () => {
|
|
322
|
+
await this.server.close();
|
|
323
|
+
process.exit(0);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private setupToolHandlers() {
|
|
328
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
329
|
+
tools: [
|
|
330
|
+
{
|
|
331
|
+
name: "hello_world",
|
|
332
|
+
description: "A simple hello world tool",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
type: "object",
|
|
335
|
+
properties: {
|
|
336
|
+
name: {
|
|
337
|
+
type: "string",
|
|
338
|
+
description: "Name to greet",
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
required: ["name"],
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
}));
|
|
346
|
+
|
|
347
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
348
|
+
if (request.params.name !== "hello_world") {
|
|
349
|
+
throw new McpError(
|
|
350
|
+
ErrorCode.MethodNotFound,
|
|
351
|
+
\`Unknown tool: \${request.params.name}\`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const args = request.params.arguments as { name: string };
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
content: [
|
|
359
|
+
{
|
|
360
|
+
type: "text",
|
|
361
|
+
text: \`Hello, \${args.name}! Welcome to your new MCP server.\`,
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
};
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async run() {
|
|
369
|
+
const transport = new StdioServerTransport();
|
|
370
|
+
await this.server.connect(transport);
|
|
371
|
+
console.error('${serverName} MCP server running on stdio');
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const server = new ${serverName.replace(/[^a-zA-Z0-9]/g, '')}Server();
|
|
376
|
+
server.run().catch(console.error);
|
|
377
|
+
`
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
for (const [filename, content] of Object.entries(filesToCreate)) {
|
|
381
|
+
await fs.writeFile(path.join(targetDir, filename), content, 'utf8');
|
|
382
|
+
console.log(` š Created ${filename}`);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
console.log(`\nš MCP Server scaffolded successfully in ./${serverName}`);
|
|
386
|
+
console.log('\nNext steps:');
|
|
387
|
+
console.log(` cd ${serverName}`);
|
|
388
|
+
console.log(' npm install');
|
|
389
|
+
console.log(' npm run build');
|
|
390
|
+
console.log('\nTo use with Claude Desktop or Cursor, add this to your config:');
|
|
391
|
+
console.log(`
|
|
392
|
+
{
|
|
393
|
+
"mcpServers": {
|
|
394
|
+
"${serverName}": {
|
|
395
|
+
"command": "node",
|
|
396
|
+
"args": ["${path.join(process.cwd(), serverName, 'build', 'index.js')}"]
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}`);
|
|
400
|
+
} catch (err) {
|
|
401
|
+
console.error(`\nā Error creating MCP Server: ${err.message}`);
|
|
402
|
+
} finally {
|
|
403
|
+
rl.close();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async function runAddSkill(skillName) {
|
|
408
|
+
if (!skillName) {
|
|
409
|
+
console.error('ā Error: Skill name is required.');
|
|
410
|
+
console.log('Usage: vibes add <skill-name>');
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const sourceDir = path.join(PLUGIN_ROOT, 'skills', skillName);
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
const exists = await fs.access(sourceDir).then(() => true).catch(() => false);
|
|
418
|
+
if (!exists) {
|
|
419
|
+
console.error(`ā Error: Skill '${skillName}' not found in vibes-plug global registry.`);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Default to .agents/skills for local project injection
|
|
424
|
+
const targetBaseDir = path.join(process.cwd(), '.agents', 'skills');
|
|
425
|
+
const targetDir = path.join(targetBaseDir, skillName);
|
|
426
|
+
|
|
427
|
+
await fs.mkdir(targetBaseDir, { recursive: true });
|
|
428
|
+
|
|
429
|
+
// Node v16.7.0+ required for fs.cp
|
|
430
|
+
await fs.cp(sourceDir, targetDir, { recursive: true });
|
|
431
|
+
|
|
432
|
+
console.log(`\nš Successfully added '${skillName}' to your local project!`);
|
|
433
|
+
console.log(`š Location: .agents/skills/${skillName}/SKILL.md`);
|
|
434
|
+
console.log('\nYour AI agents will now automatically load this skill when working in this repository.');
|
|
435
|
+
} catch (err) {
|
|
436
|
+
console.error(`\nā Error adding skill: ${err.message}`);
|
|
437
|
+
} finally {
|
|
438
|
+
rl.close();
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function runUi() {
|
|
443
|
+
let checkbox;
|
|
444
|
+
try {
|
|
445
|
+
const prompts = await import('@inquirer/prompts');
|
|
446
|
+
checkbox = prompts.checkbox;
|
|
447
|
+
} catch (err) {
|
|
448
|
+
console.error('ā Error: The Interactive TUI requires the @inquirer/prompts package.');
|
|
449
|
+
console.error('Please run "npm install" inside the vibes-plug directory first:');
|
|
450
|
+
console.error(` cd ${PLUGIN_ROOT}`);
|
|
451
|
+
console.error(' npm install');
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Close the global readline so inquirer can take over stdin
|
|
456
|
+
rl.close();
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
const skillsDir = path.join(PLUGIN_ROOT, 'skills');
|
|
460
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
461
|
+
const skills = entries
|
|
462
|
+
.filter(dirent => dirent.isDirectory())
|
|
463
|
+
.map(dirent => dirent.name)
|
|
464
|
+
.sort();
|
|
465
|
+
|
|
466
|
+
const choices = skills.map(skill => ({ name: skill, value: skill }));
|
|
467
|
+
|
|
468
|
+
console.log(`\nš Vibes-Plug Interactive TUI`);
|
|
469
|
+
const selectedSkills = await checkbox({
|
|
470
|
+
message: 'Select the AI skills to install into this project:',
|
|
471
|
+
choices: choices,
|
|
472
|
+
loop: false,
|
|
473
|
+
pageSize: 15
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
if (!selectedSkills || selectedSkills.length === 0) {
|
|
477
|
+
console.log('No skills selected. Exiting.');
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const targetBaseDir = path.join(process.cwd(), '.agents', 'skills');
|
|
482
|
+
await fs.mkdir(targetBaseDir, { recursive: true });
|
|
483
|
+
|
|
484
|
+
let count = 0;
|
|
485
|
+
for (const skill of selectedSkills) {
|
|
486
|
+
const sourceDir = path.join(skillsDir, skill);
|
|
487
|
+
const targetDir = path.join(targetBaseDir, skill);
|
|
488
|
+
|
|
489
|
+
const exists = await fs.access(targetDir).then(() => true).catch(() => false);
|
|
490
|
+
if (!exists) {
|
|
491
|
+
await fs.cp(sourceDir, targetDir, { recursive: true });
|
|
492
|
+
console.log(` ā Added: ${skill}`);
|
|
493
|
+
count++;
|
|
494
|
+
} else {
|
|
495
|
+
console.log(` āļø Skipped (already exists): ${skill}`);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
console.log(`\nš Successfully installed ${count} skill(s) into .agents/skills/`);
|
|
500
|
+
console.log('Your AI agents will now automatically load these skills when working in this repository.');
|
|
501
|
+
|
|
502
|
+
} catch (err) {
|
|
503
|
+
if (err.name === 'ExitPromptError') {
|
|
504
|
+
console.log('Interactive prompt cancelled.');
|
|
505
|
+
} else {
|
|
506
|
+
console.error(`\nā Error: ${err.message}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async function runRemoveSkill(skillName) {
|
|
512
|
+
if (!skillName) {
|
|
513
|
+
console.error('ā Error: Skill name is required.');
|
|
514
|
+
console.log('Usage: vibes remove <skill-name>');
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const targetDir = path.join(process.cwd(), '.agents', 'skills', skillName);
|
|
519
|
+
|
|
520
|
+
try {
|
|
521
|
+
const exists = await fs.access(targetDir).then(() => true).catch(() => false);
|
|
522
|
+
if (!exists) {
|
|
523
|
+
console.error(`ā Skill '${skillName}' is not installed in this project.`);
|
|
524
|
+
console.log('Run "vibes list" to see available skills or "vibes ui" to manage them.');
|
|
525
|
+
process.exit(1);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
await fs.rm(targetDir, { recursive: true, force: true });
|
|
529
|
+
console.log(`\nā
Successfully removed '${skillName}' from your local project.`);
|
|
530
|
+
console.log('š” Run "vibes add" or "vibes ui" to re-install skills anytime.');
|
|
531
|
+
|
|
532
|
+
} catch (err) {
|
|
533
|
+
console.error(`\nā Error removing skill: ${err.message}`);
|
|
534
|
+
} finally {
|
|
535
|
+
rl.close();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function runListSkills(filter) {
|
|
540
|
+
try {
|
|
541
|
+
const skillsDir = path.join(PLUGIN_ROOT, 'skills');
|
|
542
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
543
|
+
const allSkills = entries
|
|
544
|
+
.filter(dirent => dirent.isDirectory())
|
|
545
|
+
.map(dirent => dirent.name)
|
|
546
|
+
.sort();
|
|
547
|
+
|
|
548
|
+
// Check which skills are already installed locally
|
|
549
|
+
const localAgentsDir = path.join(process.cwd(), '.agents', 'skills');
|
|
550
|
+
let localSkills = new Set();
|
|
551
|
+
const localExists = await fs.access(localAgentsDir).then(() => true).catch(() => false);
|
|
552
|
+
if (localExists) {
|
|
553
|
+
const localEntries = await fs.readdir(localAgentsDir, { withFileTypes: true });
|
|
554
|
+
localEntries
|
|
555
|
+
.filter(d => d.isDirectory())
|
|
556
|
+
.forEach(d => localSkills.add(d.name));
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Apply filter if provided
|
|
560
|
+
const filtered = filter
|
|
561
|
+
? allSkills.filter(s => s.includes(filter.toLowerCase()))
|
|
562
|
+
: allSkills;
|
|
563
|
+
|
|
564
|
+
if (filtered.length === 0) {
|
|
565
|
+
console.log(`ā No skills found matching "${filter}".`);
|
|
566
|
+
rl.close();
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const title = filter ? `Skills matching "${filter}"` : 'All Available Skills';
|
|
571
|
+
console.log(`\nš Vibes-Plug Registry ā ${title}`);
|
|
572
|
+
console.log(`š ${filtered.length} skill(s) found | ā
= installed locally\n`);
|
|
573
|
+
|
|
574
|
+
// Group by domain prefix for readability
|
|
575
|
+
filtered.forEach(skill => {
|
|
576
|
+
const installed = localSkills.has(skill) ? ' ā
' : '';
|
|
577
|
+
console.log(` ⢠${skill}${installed}`);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
console.log(`
|
|
581
|
+
š” Commands:`);
|
|
582
|
+
console.log(' vibes add <skill-name> ā Install a skill into this project');
|
|
583
|
+
console.log(' vibes remove <skill-name> ā Remove a skill from this project');
|
|
584
|
+
console.log(' vibes ui ā Interactive multiselect menu');
|
|
585
|
+
|
|
586
|
+
} catch (err) {
|
|
587
|
+
console.error(`\nā Error listing skills: ${err.message}`);
|
|
588
|
+
} finally {
|
|
589
|
+
rl.close();
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
async function runVersion(action, type) {
|
|
594
|
+
const pkgPath = path.join(PLUGIN_ROOT, 'package.json');
|
|
595
|
+
const pluginJsonPath = path.join(PLUGIN_ROOT, 'plugin.json');
|
|
596
|
+
const changelogPath = path.join(PLUGIN_ROOT, 'CHANGELOG.md');
|
|
597
|
+
const vivesMjsPath = path.join(PLUGIN_ROOT, 'bin', 'vibes.mjs');
|
|
598
|
+
|
|
599
|
+
try {
|
|
600
|
+
const pkgRaw = await fs.readFile(pkgPath, 'utf8');
|
|
601
|
+
const pkg = JSON.parse(pkgRaw);
|
|
602
|
+
const current = pkg.version;
|
|
603
|
+
const [major, minor, patch] = current.split('.').map(Number);
|
|
604
|
+
|
|
605
|
+
if (!action || action === 'current') {
|
|
606
|
+
console.log(`š¦ Current version: v${current}`);
|
|
607
|
+
rl.close();
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (action !== 'bump') {
|
|
612
|
+
console.error('ā Usage: vibes version current OR vibes version bump <major|minor|patch>');
|
|
613
|
+
process.exit(1);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
let next;
|
|
617
|
+
if (type === 'major') next = `${major + 1}.0.0`;
|
|
618
|
+
else if (type === 'minor') next = `${major}.${minor + 1}.0`;
|
|
619
|
+
else if (type === 'patch') next = `${major}.${minor}.${patch + 1}`;
|
|
620
|
+
else {
|
|
621
|
+
console.error('ā Bump type must be: major | minor | patch');
|
|
622
|
+
process.exit(1);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const files = [
|
|
626
|
+
{ path: pkgPath, from: `"version": "${current}"`, to: `"version": "${next}"` },
|
|
627
|
+
{ path: pluginJsonPath, from: `"version": "${current}"`, to: `"version": "${next}"` },
|
|
628
|
+
{ path: vivesMjsPath, from: `CLI (v${current})`, to: `CLI (v${next})` },
|
|
629
|
+
];
|
|
630
|
+
|
|
631
|
+
for (const { path: filePath, from, to } of files) {
|
|
632
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
633
|
+
if (raw.includes(from)) {
|
|
634
|
+
await fs.writeFile(filePath, raw.replaceAll(from, to), 'utf8');
|
|
635
|
+
console.log(` ā
Updated: ${path.basename(filePath)} ${current} ā ${next}`);
|
|
636
|
+
} else {
|
|
637
|
+
console.log(` āļø Skipped (pattern not found): ${path.basename(filePath)}`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Prepend a new changelog entry
|
|
642
|
+
const today = new Date().toISOString().split('T')[0];
|
|
643
|
+
const entry = `## [${next}] - ${today}\n\n### Changed\n- Version bumped from ${current} to ${next}\n\n---\n\n`;
|
|
644
|
+
const changelog = await fs.readFile(changelogPath, 'utf8');
|
|
645
|
+
const insertIdx = changelog.indexOf('## [');
|
|
646
|
+
const updated = changelog.slice(0, insertIdx) + entry + changelog.slice(insertIdx);
|
|
647
|
+
await fs.writeFile(changelogPath, updated, 'utf8');
|
|
648
|
+
console.log(` ā
Updated: CHANGELOG.md added [${next}] entry`);
|
|
649
|
+
|
|
650
|
+
console.log(`
|
|
651
|
+
š Version bumped: v${current} ā v${next}`);
|
|
652
|
+
console.log('š” Don\'t forget to commit and tag: git tag v' + next);
|
|
653
|
+
|
|
654
|
+
} catch (err) {
|
|
655
|
+
console.error(`ā Error: ${err.message}`);
|
|
656
|
+
} finally {
|
|
657
|
+
rl.close();
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
async function runDoctor() {
|
|
662
|
+
console.log('\n𧬠Vibes-Plug Doctor ā Running diagnostics...\n');
|
|
663
|
+
const checks = [];
|
|
664
|
+
|
|
665
|
+
// 1. Node.js version
|
|
666
|
+
const nodeVersion = process.versions.node;
|
|
667
|
+
const nodeMajor = parseInt(nodeVersion.split('.')[0], 10);
|
|
668
|
+
checks.push(nodeMajor >= 18
|
|
669
|
+
? `ā
Node.js v${nodeVersion} (>= 18 required)`
|
|
670
|
+
: `ā Node.js v${nodeVersion} is too old. Please upgrade to v18+`);
|
|
671
|
+
|
|
672
|
+
// 2. npx available
|
|
673
|
+
try {
|
|
674
|
+
const { execSync } = await import('child_process');
|
|
675
|
+
execSync('npx --version', { stdio: 'pipe' });
|
|
676
|
+
checks.push('ā
npx is available');
|
|
677
|
+
} catch {
|
|
678
|
+
checks.push('ā npx not found. Install Node.js from https://nodejs.org');
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// 3. Global skills directory
|
|
682
|
+
const skillsDir = path.join(PLUGIN_ROOT, 'skills');
|
|
683
|
+
try {
|
|
684
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
685
|
+
const count = entries.filter(d => d.isDirectory()).length;
|
|
686
|
+
checks.push(`ā
Global skills registry: ${count} skills found`);
|
|
687
|
+
} catch {
|
|
688
|
+
checks.push('ā Skills directory missing! Run: npm install -g vibes-plug');
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// 4. @inquirer/prompts installed
|
|
692
|
+
try {
|
|
693
|
+
await import('@inquirer/prompts');
|
|
694
|
+
checks.push('ā
@inquirer/prompts is installed (vibes ui works)');
|
|
695
|
+
} catch {
|
|
696
|
+
checks.push(`ā ļø @inquirer/prompts not found. Run: cd ${PLUGIN_ROOT} && npm install`);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// 5. Local project .agents/skills
|
|
700
|
+
const localAgentsDir = path.join(process.cwd(), '.agents', 'skills');
|
|
701
|
+
const localExists = await fs.access(localAgentsDir).then(() => true).catch(() => false);
|
|
702
|
+
if (localExists) {
|
|
703
|
+
const localEntries = await fs.readdir(localAgentsDir, { withFileTypes: true });
|
|
704
|
+
const localCount = localEntries.filter(d => d.isDirectory()).length;
|
|
705
|
+
checks.push(`ā
Local project: ${localCount} skill(s) installed in .agents/skills/`);
|
|
706
|
+
} else {
|
|
707
|
+
checks.push('ā¹ļø No local .agents/skills/ found (run vibes add or vibes ui to install skills)');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// 6. Package version
|
|
711
|
+
const pkgPath = path.join(PLUGIN_ROOT, 'package.json');
|
|
712
|
+
const pkgRaw = await fs.readFile(pkgPath, 'utf8');
|
|
713
|
+
const pkg = JSON.parse(pkgRaw);
|
|
714
|
+
checks.push(`š¦ vibes-plug version: v${pkg.version}`);
|
|
715
|
+
|
|
716
|
+
checks.forEach(c => console.log(' ' + c));
|
|
717
|
+
|
|
718
|
+
const failed = checks.filter(c => c.startsWith(' ā')).length;
|
|
719
|
+
console.log(failed > 0
|
|
720
|
+
? `\nšØ ${failed} issue(s) found. Fix them above and re-run: vibes doctor`
|
|
721
|
+
: '\nš All checks passed! Your Vibes-Plug environment is healthy.');
|
|
722
|
+
|
|
723
|
+
rl.close();
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
async function runSkillInfo(skillName) {
|
|
727
|
+
if (!skillName) {
|
|
728
|
+
console.error('ā Error: Skill name is required.');
|
|
729
|
+
console.log('Usage: vibes skill info <skill-name>');
|
|
730
|
+
process.exit(1);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const skillPath = path.join(PLUGIN_ROOT, 'skills', skillName, 'SKILL.md');
|
|
734
|
+
try {
|
|
735
|
+
const content = await fs.readFile(skillPath, 'utf8');
|
|
736
|
+
|
|
737
|
+
// Parse frontmatter
|
|
738
|
+
const fmMatch = content.match(/---[\r\n]+([\s\S]*?)[\r\n]+---/);
|
|
739
|
+
if (!fmMatch) {
|
|
740
|
+
console.error(`ā Skill '${skillName}' has no frontmatter.`);
|
|
741
|
+
process.exit(1);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const fm = fmMatch[1];
|
|
745
|
+
const get = (key) => { const m = fm.match(new RegExp(`${key}:\\s*["']?([^"'\\n]+)["']?`)); return m ? m[1].trim() : 'N/A'; };
|
|
746
|
+
|
|
747
|
+
const name = get('name');
|
|
748
|
+
const version = get('version');
|
|
749
|
+
const description = get('description');
|
|
750
|
+
const author = get('author');
|
|
751
|
+
|
|
752
|
+
// Extract orchestration section
|
|
753
|
+
const orchMatch = content.match(/## (?:Orchestration & Integration|Integrasi Orkestrasi)[\r\n]+([\s\S]*?)(?=\n##|$)/);
|
|
754
|
+
const orchText = orchMatch ? orchMatch[1].trim().split('\n').slice(0, 5).join('\n') : 'N/A';
|
|
755
|
+
|
|
756
|
+
// Check local installation status
|
|
757
|
+
const localPath = path.join(process.cwd(), '.agents', 'skills', skillName);
|
|
758
|
+
const isLocal = await fs.access(localPath).then(() => true).catch(() => false);
|
|
759
|
+
|
|
760
|
+
console.log(`\nš ${name} (v${version})`);
|
|
761
|
+
console.log(`š ${description}`);
|
|
762
|
+
console.log(`š¤ Author : ${author}`);
|
|
763
|
+
console.log(`š Status : ${isLocal ? 'ā
Installed locally (.agents/skills/)' : 'ā Not installed in this project'}`);
|
|
764
|
+
console.log(`\nš Orchestration:`);
|
|
765
|
+
console.log(orchText);
|
|
766
|
+
console.log(`\nš” To install: vibes add ${skillName}`);
|
|
767
|
+
|
|
768
|
+
} catch {
|
|
769
|
+
console.error(`ā Skill '${skillName}' not found in the global registry.`);
|
|
770
|
+
console.log('Run "vibes list" to see all available skills.');
|
|
771
|
+
} finally {
|
|
772
|
+
rl.close();
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
async function runHooks(action) {
|
|
777
|
+
const gitDir = path.join(process.cwd(), '.git');
|
|
778
|
+
const hookPath = path.join(gitDir, 'hooks', 'pre-commit');
|
|
779
|
+
|
|
780
|
+
const gitExists = await fs.access(gitDir).then(() => true).catch(() => false);
|
|
781
|
+
if (!gitExists) {
|
|
782
|
+
console.error('ā No .git directory found. Run this command inside a git repository.');
|
|
783
|
+
process.exit(1);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
try {
|
|
787
|
+
if (!action || action === 'install') {
|
|
788
|
+
const hookContent = `#!/bin/sh
|
|
789
|
+
# Installed by vibes-plug ā Anti-AI Slop pre-commit gate
|
|
790
|
+
echo "š Running Vibes Anti-Slop Audit..."
|
|
791
|
+
node "${path.join(PLUGIN_ROOT, 'scripts', 'check-anti-slop.mjs')}" || exit 1
|
|
792
|
+
`;
|
|
793
|
+
await fs.mkdir(path.join(gitDir, 'hooks'), { recursive: true });
|
|
794
|
+
await fs.writeFile(hookPath, hookContent, { mode: 0o755 });
|
|
795
|
+
console.log('\n\u2705 Git pre-commit hook installed!');
|
|
796
|
+
console.log('š”ļø Anti-Slop Audit will now run automatically before every commit.');
|
|
797
|
+
console.log(`š Hook location: ${hookPath}`);
|
|
798
|
+
|
|
799
|
+
} else if (action === 'remove') {
|
|
800
|
+
const exists = await fs.access(hookPath).then(() => true).catch(() => false);
|
|
801
|
+
if (!exists) {
|
|
802
|
+
console.log('ā¹ļø No vibes pre-commit hook found to remove.');
|
|
803
|
+
} else {
|
|
804
|
+
await fs.rm(hookPath, { force: true });
|
|
805
|
+
console.log('ā
Git pre-commit hook removed.');
|
|
806
|
+
}
|
|
807
|
+
} else {
|
|
808
|
+
console.error('ā Usage: vibes hooks install OR vibes hooks remove');
|
|
809
|
+
process.exit(1);
|
|
810
|
+
}
|
|
811
|
+
} catch (err) {
|
|
812
|
+
console.error(`ā Error managing hooks: ${err.message}`);
|
|
813
|
+
} finally {
|
|
814
|
+
rl.close();
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Built-in skill recipes (curated bundles)
|
|
819
|
+
const RECIPES = {
|
|
820
|
+
'ai-stack': {
|
|
821
|
+
description: 'Full AI/LLM integration stack with RAG, vector DB, and agent orchestration',
|
|
822
|
+
skills: ['ai-llm-integration-expert', 'vector-db-rag-expert', 'vercel-ai-sdk-expert', 'ai-prompt-engineering-expert', 'multi-agent-orchestration']
|
|
823
|
+
},
|
|
824
|
+
'fullstack-pro': {
|
|
825
|
+
description: 'Production-grade fullstack: Next.js 15, auth, DB, payments, testing',
|
|
826
|
+
skills: ['nextjs-app-router-expert', 'authentication-identity-expert', 'database-orm-expert', 'payment-gateway-expert', 'e2e-testing-expert', 'error-resilience-expert']
|
|
827
|
+
},
|
|
828
|
+
'security-hardened': {
|
|
829
|
+
description: 'Security-first stack: zero-trust, rate limiting, auth, GDPR compliance',
|
|
830
|
+
skills: ['zero-trust-secret-vault', 'rate-limit-abuse-prevention', 'authentication-identity-expert', 'compliance-gdpr-privacy-expert', 'autonomous-red-teamer', 'supabase-security-expert']
|
|
831
|
+
},
|
|
832
|
+
'realtime-app': {
|
|
833
|
+
description: 'Real-time collaboration: WebSockets, SSE, CRDTs, streaming',
|
|
834
|
+
skills: ['realtime-collaboration-expert', 'sse-websocket-streaming-expert', 'state-management-expert', 'error-resilience-expert']
|
|
835
|
+
},
|
|
836
|
+
'mobile-first': {
|
|
837
|
+
description: 'Mobile app stack: Expo, offline-first PWA, push notifications',
|
|
838
|
+
skills: ['mobile-expo-expert', 'pwa-offline-first-expert', 'authentication-identity-expert', 'api-design-expert', 'email-notification-expert']
|
|
839
|
+
},
|
|
840
|
+
'data-platform': {
|
|
841
|
+
description: 'Data engineering: ETL pipelines, visualization, telemetry, analytics',
|
|
842
|
+
skills: ['data-pipeline-etl-expert', 'data-visualization-expert', 'data-telemetry-expert', 'search-engine-expert']
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
async function runRecipe(action, recipeName) {
|
|
847
|
+
if (!action || action === 'list') {
|
|
848
|
+
console.log('\n\ud83e\uddc9 Available Vibes-Plug Recipes:\n');
|
|
849
|
+
for (const [name, { description, skills }] of Object.entries(RECIPES)) {
|
|
850
|
+
console.log(` \x1b[36m${name}\x1b[0m`);
|
|
851
|
+
console.log(` ${description}`);
|
|
852
|
+
console.log(` Skills (${skills.length}): ${skills.join(', ')}\n`);
|
|
853
|
+
}
|
|
854
|
+
console.log('Usage: vibes recipe apply <recipe-name>');
|
|
855
|
+
rl.close();
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
if (action === 'apply') {
|
|
860
|
+
if (!recipeName) {
|
|
861
|
+
console.error('\u274c Error: Recipe name required.');
|
|
862
|
+
console.log('Usage: vibes recipe apply <recipe-name>');
|
|
863
|
+
console.log('Run "vibes recipe list" to see available recipes.');
|
|
864
|
+
process.exit(1);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const recipe = RECIPES[recipeName];
|
|
868
|
+
if (!recipe) {
|
|
869
|
+
console.error(`\u274c Recipe '${recipeName}' not found.`);
|
|
870
|
+
console.log(`Available: ${Object.keys(RECIPES).join(', ')}`);
|
|
871
|
+
process.exit(1);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
console.log(`\n\ud83e\uddc9 Applying recipe: ${recipeName}`);
|
|
875
|
+
console.log(`\ud83d\udcdd ${recipe.description}\n`);
|
|
876
|
+
|
|
877
|
+
const targetAgentsDir = path.join(process.cwd(), '.agents', 'skills');
|
|
878
|
+
await fs.mkdir(targetAgentsDir, { recursive: true });
|
|
879
|
+
|
|
880
|
+
let installed = 0, skipped = 0;
|
|
881
|
+
for (const skill of recipe.skills) {
|
|
882
|
+
const sourceDir = path.join(PLUGIN_ROOT, 'skills', skill);
|
|
883
|
+
const targetDir = path.join(targetAgentsDir, skill);
|
|
884
|
+
const alreadyInstalled = await fs.access(targetDir).then(() => true).catch(() => false);
|
|
885
|
+
if (alreadyInstalled) {
|
|
886
|
+
console.log(` \u23ed\ufe0f Already installed: ${skill}`);
|
|
887
|
+
skipped++;
|
|
888
|
+
} else {
|
|
889
|
+
const exists = await fs.access(sourceDir).then(() => true).catch(() => false);
|
|
890
|
+
if (exists) {
|
|
891
|
+
await fs.cp(sourceDir, targetDir, { recursive: true });
|
|
892
|
+
console.log(` \u2705 Installed: ${skill}`);
|
|
893
|
+
installed++;
|
|
894
|
+
} else {
|
|
895
|
+
console.warn(` \u26a0\ufe0f Not found in registry: ${skill}`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
console.log(`\n\ud83c\udf89 Recipe '${recipeName}' applied! ${installed} new, ${skipped} already installed.`);
|
|
901
|
+
console.log('\ud83d\udca1 Ask your AI agent to read the injected skills and begin development.');
|
|
902
|
+
|
|
903
|
+
} else {
|
|
904
|
+
console.error('\u274c Usage: vibes recipe list OR vibes recipe apply <name>');
|
|
905
|
+
process.exit(1);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
rl.close();
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
async function runSearch(query) {
|
|
912
|
+
if (!query) {
|
|
913
|
+
console.error('\u274c Error: Search query required.');
|
|
914
|
+
console.log('Usage: vibes search <query>');
|
|
915
|
+
process.exit(1);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const skillsDir = path.join(PLUGIN_ROOT, 'skills');
|
|
919
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
920
|
+
const skills = entries.filter(d => d.isDirectory()).map(d => d.name);
|
|
921
|
+
|
|
922
|
+
const q = query.toLowerCase();
|
|
923
|
+
const results = [];
|
|
924
|
+
|
|
925
|
+
for (const skill of skills) {
|
|
926
|
+
const skillMdPath = path.join(skillsDir, skill, 'SKILL.md');
|
|
927
|
+
try {
|
|
928
|
+
const content = await fs.readFile(skillMdPath, 'utf8');
|
|
929
|
+
const lower = content.toLowerCase();
|
|
930
|
+
// Score by keyword frequency
|
|
931
|
+
const score = (lower.match(new RegExp(q, 'g')) || []).length;
|
|
932
|
+
if (score > 0) {
|
|
933
|
+
// Extract description from frontmatter
|
|
934
|
+
const descMatch = content.match(/description:\s*["']?([^"'\n]{10,120})/);
|
|
935
|
+
const desc = descMatch ? descMatch[1].trim() : '';
|
|
936
|
+
results.push({ skill, score, desc });
|
|
937
|
+
}
|
|
938
|
+
} catch { /* skip unreadable */ }
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
results.sort((a, b) => b.score - a.score);
|
|
942
|
+
|
|
943
|
+
if (results.length === 0) {
|
|
944
|
+
console.log(`\n\ud83d\udd0d No skills found matching "${query}".`);
|
|
945
|
+
console.log('Try: vibes list to browse all 125 skills.');
|
|
946
|
+
} else {
|
|
947
|
+
console.log(`\n\ud83d\udd0d Found ${results.length} skill(s) matching "${query}":\n`);
|
|
948
|
+
results.slice(0, 10).forEach(({ skill, score, desc }) => {
|
|
949
|
+
console.log(` \x1b[36m${skill}\x1b[0m (relevance: ${score})`);
|
|
950
|
+
if (desc) console.log(` ${desc.slice(0, 100)}`);
|
|
951
|
+
});
|
|
952
|
+
if (results.length > 10) console.log(` ... and ${results.length - 10} more. Use "vibes list" to see all.`);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
rl.close();
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
async function runSync() {
|
|
959
|
+
console.log('\nš Vibes-Plug Master Synchronizer (Pemicu Utama)...');
|
|
960
|
+
console.log('Verifying primary trigger configuration and cross-skill synchronization...\n');
|
|
961
|
+
|
|
962
|
+
const skillsDir = path.join(PLUGIN_ROOT, 'skills');
|
|
963
|
+
let skillCount = 0;
|
|
964
|
+
try {
|
|
965
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
966
|
+
skillCount = entries.filter(e => e.isDirectory()).length;
|
|
967
|
+
} catch (e) {
|
|
968
|
+
console.error(`ā Could not read skills dir: ${e.message}`);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
console.log(` ā
Global Registry: ${skillCount} skills detected.`);
|
|
972
|
+
|
|
973
|
+
// Check platforms entry points
|
|
974
|
+
const checkFiles = [
|
|
975
|
+
{ name: 'Antigravity (AGENTS.md)', file: path.join(PLUGIN_ROOT, 'AGENTS.md') },
|
|
976
|
+
{ name: 'Claude Code (CLAUDE.md)', file: path.join(PLUGIN_ROOT, 'CLAUDE.md') },
|
|
977
|
+
{ name: 'Cursor IDE (.cursorrules)', file: path.join(PLUGIN_ROOT, '.cursorrules') },
|
|
978
|
+
{ name: 'Cursor Rules (.cursor/rules/vibes-plug-core.mdc)', file: path.join(PLUGIN_ROOT, '.cursor', 'rules', 'vibes-plug-core.mdc') },
|
|
979
|
+
{ name: 'Claude Rules (.claude/rules/vibes-plug-core.md)', file: path.join(PLUGIN_ROOT, '.claude', 'rules', 'vibes-plug-core.md') }
|
|
980
|
+
];
|
|
981
|
+
|
|
982
|
+
for (const item of checkFiles) {
|
|
983
|
+
try {
|
|
984
|
+
const content = await fs.readFile(item.file, 'utf8');
|
|
985
|
+
const hasTrigger = content.includes('PRIMARY TRIGGER') || content.includes('Pemicu Utama');
|
|
986
|
+
if (hasTrigger) {
|
|
987
|
+
console.log(` ā
${item.name}: Primary Trigger active & configured.`);
|
|
988
|
+
} else {
|
|
989
|
+
console.log(` ā ļø ${item.name}: Missing Primary Trigger directive.`);
|
|
990
|
+
}
|
|
991
|
+
} catch {
|
|
992
|
+
console.log(` ā¹ļø ${item.name}: Not found.`);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// Check master orchestrators
|
|
997
|
+
const brainPath = path.join(skillsDir, 'brainstorming', 'SKILL.md');
|
|
998
|
+
const ztpPath = path.join(skillsDir, 'zero-to-prod-orchestrator', 'SKILL.md');
|
|
999
|
+
const hasBrain = await fs.access(brainPath).then(() => true).catch(() => false);
|
|
1000
|
+
const hasZtp = await fs.access(ztpPath).then(() => true).catch(() => false);
|
|
1001
|
+
|
|
1002
|
+
if (hasBrain && hasZtp) {
|
|
1003
|
+
console.log(` ā
Master Orchestrators: brainstorming & zero-to-prod-orchestrator synchronized.`);
|
|
1004
|
+
} else {
|
|
1005
|
+
console.log(` ā ļø Master Orchestrators: check missing files.`);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
console.log('\nā” Vibes-Plug is operational as the PRIMARY TRIGGER (Pemicu Utama).');
|
|
1009
|
+
console.log(' All 127+ skills automatically synchronize when the user submits any coding prompt.\n');
|
|
1010
|
+
rl.close();
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
function showHelp() {
|
|
1014
|
+
console.log(`
|
|
1015
|
+
š Vibes-Plug CLI (v3.9.0)
|
|
1016
|
+
The ultimate AI Swarm Orchestrator tool.
|
|
1017
|
+
|
|
1018
|
+
Usage:
|
|
1019
|
+
vibes <command> [options]
|
|
1020
|
+
|
|
1021
|
+
Commands:
|
|
1022
|
+
sync Verify and synchronize primary trigger & cross-skill orchestration
|
|
1023
|
+
init <project-name> Scaffold a new zero-to-prod 8-Phase project structure
|
|
1024
|
+
bootstrap <type> <name> Super-scaffold a project + AI Skills (saas | ecommerce | mobile | api | fullstack)
|
|
1025
|
+
ui Launch the interactive TUI to visually select and install skills
|
|
1026
|
+
list [filter] List all available skills (optional: filter by keyword)
|
|
1027
|
+
search <query> Search all 131 skills by keyword (ranked by relevance)
|
|
1028
|
+
add <skill-name> Inject a skill from the global registry into your local project
|
|
1029
|
+
remove <skill-name> Remove an installed skill from your local project (.agents/skills)
|
|
1030
|
+
skill info <skill-name> Show metadata, description, and orchestration info for a skill
|
|
1031
|
+
recipe list List all built-in skill bundles/recipes
|
|
1032
|
+
recipe apply <name> Apply a recipe bundle to your project (ai-stack, fullstack-pro, ...)
|
|
1033
|
+
create-skill <skill-name> Scaffold a new skill using the standard template (kebab-case)
|
|
1034
|
+
create-mcp <server-name> Scaffold a new Model Context Protocol (MCP) server
|
|
1035
|
+
version current Show current version
|
|
1036
|
+
version bump <type> Bump version across all files (type: major | minor | patch)
|
|
1037
|
+
hooks install Install Git pre-commit Anti-Slop gate
|
|
1038
|
+
hooks remove Remove the Git pre-commit hook
|
|
1039
|
+
doctor Run environment health diagnostics
|
|
1040
|
+
audit Run the strict Anti-AI Slop quality gate check
|
|
1041
|
+
validate Run the strict skill ecosystem validation check
|
|
1042
|
+
help Show this help menu
|
|
1043
|
+
`);
|
|
1044
|
+
rl.close();
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// Main Router
|
|
1048
|
+
switch (command) {
|
|
1049
|
+
case 'sync':
|
|
1050
|
+
runSync();
|
|
1051
|
+
break;
|
|
1052
|
+
case 'init':
|
|
1053
|
+
runInit(args[1]);
|
|
1054
|
+
break;
|
|
1055
|
+
case 'bootstrap':
|
|
1056
|
+
runBootstrap(args[1], args[2]);
|
|
1057
|
+
break;
|
|
1058
|
+
case 'ui':
|
|
1059
|
+
runUi();
|
|
1060
|
+
break;
|
|
1061
|
+
case 'list':
|
|
1062
|
+
runListSkills(args[1]);
|
|
1063
|
+
break;
|
|
1064
|
+
case 'search':
|
|
1065
|
+
runSearch(args[1]);
|
|
1066
|
+
break;
|
|
1067
|
+
case 'add':
|
|
1068
|
+
runAddSkill(args[1]);
|
|
1069
|
+
break;
|
|
1070
|
+
case 'remove':
|
|
1071
|
+
runRemoveSkill(args[1]);
|
|
1072
|
+
break;
|
|
1073
|
+
case 'recipe':
|
|
1074
|
+
runRecipe(args[1], args[2]);
|
|
1075
|
+
break;
|
|
1076
|
+
case 'version':
|
|
1077
|
+
runVersion(args[1], args[2]);
|
|
1078
|
+
break;
|
|
1079
|
+
case 'doctor':
|
|
1080
|
+
runDoctor();
|
|
1081
|
+
break;
|
|
1082
|
+
case 'skill':
|
|
1083
|
+
runSkillInfo(args[2]);
|
|
1084
|
+
break;
|
|
1085
|
+
case 'hooks':
|
|
1086
|
+
runHooks(args[1]);
|
|
1087
|
+
break;
|
|
1088
|
+
case 'create-skill':
|
|
1089
|
+
runCreateSkill(args[1]);
|
|
1090
|
+
break;
|
|
1091
|
+
case 'create-mcp':
|
|
1092
|
+
runCreateMcp(args[1]);
|
|
1093
|
+
break;
|
|
1094
|
+
case 'audit':
|
|
1095
|
+
runAudit();
|
|
1096
|
+
break;
|
|
1097
|
+
case 'validate':
|
|
1098
|
+
runValidate();
|
|
1099
|
+
break;
|
|
1100
|
+
default:
|
|
1101
|
+
showHelp();
|
|
1102
|
+
break;
|
|
1103
|
+
}
|
|
1104
|
+
|