@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.
Files changed (2) hide show
  1. package/index.js +121 -65
  2. 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.0.0');
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
- // Map known Mockingbird prefixes to their actual GitHub raw paths
26
- // e.g., pytorch-skills/document-public-apis -> https://raw.githubusercontent.com/pytorch/pytorch/main/.claude/skills/document-public-apis/SKILL.md
27
-
28
- // First, try to see if it matches our known internal mapping
29
- const repoMap = {
30
- "anthropic-skills": "anthropics/skills/main/skills",
31
- "anthropics": "anthropics/skills/main/skills",
32
- "awesome-copilot": "github/awesome-copilot/main/skills",
33
- "github": "github/awesome-copilot/main/skills",
34
- "google-gemini": "google-gemini/gemini-cli/main/.gemini/skills",
35
- "gemini-cli": "google-gemini/gemini-cli/main/.gemini/skills",
36
- "openai-skills": "openai/skills/main/skills",
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
- let skillContent = null;
78
- let successfulUrl = '';
79
-
80
- for (const url of urlsToTry) {
81
- try {
82
- process.stdout.write(chalk.gray(`Probing: ${url} ... `));
83
- const response = await axios.get(url, { timeout: 5000 });
84
- skillContent = response.data;
85
- successfulUrl = url;
86
- console.log(chalk.green('OK'));
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
- if (!skillContent) {
94
- console.error(chalk.red(`\n✖ Error: Failed to find SKILL.md for '${slug}' after trying ${urlsToTry.length} locations.`));
95
- console.error(chalk.gray(`Please verify the slug and ensure the repository contains a SKILL.md file.`));
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mockingbird_konwlage_cli/cli",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Mockingbird AI Developer Client CLI",
5
5
  "main": "index.js",
6
6
  "bin": {