@mockingbird_konwlage_cli/cli 1.0.1 → 1.2.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/index.js +121 -65
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -11,88 +11,68 @@ const program = new Command();
|
|
|
11
11
|
program
|
|
12
12
|
.name('mockingbird-cli')
|
|
13
13
|
.description('Installs and manages AI skills for various coding clients')
|
|
14
|
-
.version('1.
|
|
14
|
+
.version('1.2.0');
|
|
15
15
|
|
|
16
16
|
program
|
|
17
17
|
.command('add <slug>')
|
|
18
18
|
.description('Add an AI skill by its Mockingbird slug or GitHub <org>/<repo>')
|
|
19
19
|
.option('--agent <agent>', 'The AI client to install the skill for (e.g., cursor, windsurf, claude-code)')
|
|
20
|
+
.option('--remote', 'Use the remote API instead of local')
|
|
20
21
|
.action(async (slug, options) => {
|
|
22
|
+
// ... (existing add logic)
|
|
21
23
|
const agent = (options.agent || 'cursor').toLowerCase();
|
|
24
|
+
const isRemote = options.remote || false;
|
|
22
25
|
|
|
23
26
|
console.log(chalk.blue(`[Mockingbird] Installing skill '${slug}' for agent '${agent}'...`));
|
|
24
27
|
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"openai": "openai/skills/main/skills",
|
|
38
|
-
"playwright-skills": "microsoft/playwright/main/packages/playwright/src/skill",
|
|
39
|
-
"microsoft": "microsoft/playwright/main/packages/playwright/src/skill",
|
|
40
|
-
"pytorch-skills": "pytorch/pytorch/main/.claude/skills",
|
|
41
|
-
"pytorch": "pytorch/pytorch/main/.claude/skills",
|
|
42
|
-
"antfu-skills": "antfu/skills/main/skills",
|
|
43
|
-
"antfu": "antfu/skills/main/skills",
|
|
44
|
-
"dimillian-skills": "Dimillian/Skills/main",
|
|
45
|
-
"dimillian": "Dimillian/Skills/main",
|
|
46
|
-
"prompts-chat-skills": "f/prompts.chat/main/.windsurf/skills",
|
|
47
|
-
"f": "f/prompts.chat/main/.windsurf/skills",
|
|
48
|
-
"huggingface-skills": "huggingface/skills/main/skills",
|
|
49
|
-
"huggingface": "huggingface/skills/main/skills"
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
let urlsToTry = [];
|
|
53
|
-
|
|
54
|
-
if (slug.includes('/')) {
|
|
55
|
-
const parts = slug.split('/');
|
|
56
|
-
const prefix = parts[0];
|
|
57
|
-
const skillName = parts[1];
|
|
58
|
-
|
|
59
|
-
if (repoMap[prefix]) {
|
|
60
|
-
urlsToTry.push(`https://raw.githubusercontent.com/${repoMap[prefix]}/${skillName}/SKILL.md`);
|
|
61
|
-
// Also try master just in case the mapping branch is old
|
|
62
|
-
urlsToTry.push(`https://raw.githubusercontent.com/${repoMap[prefix].replace('/main/', '/master/')}/${skillName}/SKILL.md`);
|
|
63
|
-
} else {
|
|
64
|
-
// Generic fallback for org/repo/skillname
|
|
65
|
-
let filePath = parts.slice(2).join('/') || 'SKILL.md';
|
|
66
|
-
if (!filePath.endsWith('.md')) {
|
|
67
|
-
filePath += filePath ? '/SKILL.md' : 'SKILL.md';
|
|
28
|
+
// Configuration discovery logic
|
|
29
|
+
let apiBaseUrl = process.env.MOCKINGBIRD_API_URL;
|
|
30
|
+
|
|
31
|
+
if (!apiBaseUrl) {
|
|
32
|
+
// Check .mockingbirdrc in current directory
|
|
33
|
+
const rcPath = path.join(process.cwd(), '.mockingbirdrc');
|
|
34
|
+
if (fs.existsSync(rcPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const rc = JSON.parse(fs.readFileSync(rcPath, 'utf8'));
|
|
37
|
+
apiBaseUrl = isRemote ? rc.RemoteApiBaseUrl : rc.ApiBaseUrl;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
console.warn(chalk.yellow(`[Warning] Failed to parse .mockingbirdrc: ${e.message}`));
|
|
68
40
|
}
|
|
69
|
-
urlsToTry.push(`https://raw.githubusercontent.com/${parts[0]}/${parts[1]}/main/${filePath}`);
|
|
70
|
-
urlsToTry.push(`https://raw.githubusercontent.com/${parts[0]}/${parts[1]}/master/${filePath}`);
|
|
71
41
|
}
|
|
72
|
-
} else {
|
|
73
|
-
urlsToTry.push(`https://raw.githubusercontent.com/mockingbird-ai/skills/main/${slug}/SKILL.md`);
|
|
74
|
-
urlsToTry.push(`https://raw.githubusercontent.com/mockingbird-ai/skills/master/${slug}/SKILL.md`);
|
|
75
42
|
}
|
|
76
43
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
break;
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.log(chalk.red('NOT FOUND'));
|
|
44
|
+
if (!apiBaseUrl) {
|
|
45
|
+
// Check appsettings.json in current directory
|
|
46
|
+
const appSettingsPath = path.join(process.cwd(), 'appsettings.json');
|
|
47
|
+
if (fs.existsSync(appSettingsPath)) {
|
|
48
|
+
try {
|
|
49
|
+
const settings = JSON.parse(fs.readFileSync(appSettingsPath, 'utf8'));
|
|
50
|
+
apiBaseUrl = settings.ApiBaseUrl;
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// Ignore parsing errors for general appsettings
|
|
53
|
+
}
|
|
90
54
|
}
|
|
91
55
|
}
|
|
92
56
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
57
|
+
// Final fallback to local development port
|
|
58
|
+
const API_BASE_URL = apiBaseUrl || 'http://localhost:5287';
|
|
59
|
+
const apiUrl = `${API_BASE_URL}/api/public/skills/raw/${slug}`;
|
|
60
|
+
|
|
61
|
+
let skillContent = null;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
process.stdout.write(chalk.gray(`Fetching from Mockingbird API: ${apiUrl} ... `));
|
|
65
|
+
const response = await axios.get(apiUrl, { timeout: 10000 });
|
|
66
|
+
skillContent = response.data;
|
|
67
|
+
console.log(chalk.green('OK'));
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.log(chalk.red('NOT FOUND'));
|
|
70
|
+
console.error(chalk.red(`\n✖ Error: Failed to find SKILL.md for '${slug}' from Mockingbird Knowledge Base.`));
|
|
71
|
+
if (error.response && error.response.status === 404) {
|
|
72
|
+
console.error(chalk.gray(`The skill might not be registered or supported yet at ${API_BASE_URL}`));
|
|
73
|
+
} else {
|
|
74
|
+
console.error(chalk.gray(`Details: ${error.message}`));
|
|
75
|
+
}
|
|
96
76
|
process.exit(1);
|
|
97
77
|
}
|
|
98
78
|
|
|
@@ -109,6 +89,82 @@ program
|
|
|
109
89
|
}
|
|
110
90
|
});
|
|
111
91
|
|
|
92
|
+
program
|
|
93
|
+
.command('push <path>')
|
|
94
|
+
.description('Push a local skill to the Mockingbird Knowledge Base')
|
|
95
|
+
.option('--no-assets', 'Skip bundling assets (images, etc.) and only push the SKILL.md content')
|
|
96
|
+
.action(async (skillPath, options) => {
|
|
97
|
+
const fullPath = path.resolve(skillPath);
|
|
98
|
+
const skillMdPath = fs.statSync(fullPath).isDirectory()
|
|
99
|
+
? path.join(fullPath, 'SKILL.md')
|
|
100
|
+
: fullPath;
|
|
101
|
+
|
|
102
|
+
if (!fs.existsSync(skillMdPath)) {
|
|
103
|
+
console.error(chalk.red(`✖ Error: SKILL.md not found at ${skillMdPath}`));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const skillDir = path.dirname(skillMdPath);
|
|
108
|
+
const skillName = path.basename(skillDir);
|
|
109
|
+
|
|
110
|
+
console.log(chalk.blue(`[Mockingbird] Pushing skill '${skillName}' to knowledge base...`));
|
|
111
|
+
|
|
112
|
+
let content = fs.readFileSync(skillMdPath, 'utf8');
|
|
113
|
+
|
|
114
|
+
// Inject includeAssets flag if --no-assets is used
|
|
115
|
+
if (!options.assets) {
|
|
116
|
+
console.log(chalk.yellow(`[Config] Assets will be excluded from this push.`));
|
|
117
|
+
if (content.startsWith('---')) {
|
|
118
|
+
// Insert into existing yaml
|
|
119
|
+
if (!content.includes('includeAssets:')) {
|
|
120
|
+
content = content.replace('---', `---\nincludeAssets: false`);
|
|
121
|
+
} else {
|
|
122
|
+
content = content.replace(/includeAssets:\s*true/g, 'includeAssets: false');
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
content = `---\nincludeAssets: false\n---\n\n` + content;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Deployment Logic for Dev Environment: Copy to Transmuter_Workshop/Raw_Incoming/Skills
|
|
130
|
+
// In a real production scenario, this would be a POST request to an API.
|
|
131
|
+
const SLN_ROOT = path.resolve(__dirname, '..');
|
|
132
|
+
const INCOMING_DIR = path.join(SLN_ROOT, 'Transmuter_Workshop', 'Raw_Incoming', 'Skills', 'local-publish', skillName);
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
if (!fs.existsSync(INCOMING_DIR)) {
|
|
136
|
+
fs.mkdirSync(INCOMING_DIR, { recursive: true });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (options.assets) {
|
|
140
|
+
console.log(chalk.gray(`Bundling full directory: ${skillDir} -> ${INCOMING_DIR}`));
|
|
141
|
+
copyFolderSync(skillDir, INCOMING_DIR);
|
|
142
|
+
// Overwrite with the potentially modified SKILL.md (though here it's only modified if no-assets)
|
|
143
|
+
fs.writeFileSync(path.join(INCOMING_DIR, 'SKILL.md'), content);
|
|
144
|
+
} else {
|
|
145
|
+
console.log(chalk.gray(`Bundling meta only: ${skillMdPath} -> ${INCOMING_DIR}/SKILL.md`));
|
|
146
|
+
fs.writeFileSync(path.join(INCOMING_DIR, 'SKILL.md'), content);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log(chalk.green(`\n✓ Successfully pushed skill '${skillName}' to workdesk.`));
|
|
150
|
+
console.log(chalk.gray(`The Transmuter service will process it shortly.`));
|
|
151
|
+
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error(chalk.red(`\n✖ Error: Failed to push skill. ${error.message}`));
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
function copyFolderSync(from, to) {
|
|
158
|
+
fs.mkdirSync(to, { recursive: true });
|
|
159
|
+
fs.readdirSync(from).forEach(element => {
|
|
160
|
+
if (fs.lstatSync(path.join(from, element)).isFile()) {
|
|
161
|
+
fs.copyFileSync(path.join(from, element), path.join(to, element));
|
|
162
|
+
} else {
|
|
163
|
+
copyFolderSync(path.join(from, element), path.join(to, element));
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
112
168
|
async function installForAgent(agent, slug, content) {
|
|
113
169
|
const cwd = process.cwd();
|
|
114
170
|
// Use the last part of the slug as a clean filename basis
|