natureco-cli 1.0.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.
@@ -0,0 +1,188 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+ const { CONFIG_DIR, loadConfig, saveConfig } = require('./config');
5
+
6
+ // MCP templates
7
+ const MCP_TEMPLATES = {
8
+ filesystem: {
9
+ name: 'filesystem',
10
+ description: 'File system operations (read, write, list files)',
11
+ command: 'npx',
12
+ args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
13
+ env: {},
14
+ },
15
+ github: {
16
+ name: 'github',
17
+ description: 'GitHub repository operations (issues, PRs, commits)',
18
+ command: 'npx',
19
+ args: ['-y', '@modelcontextprotocol/server-github'],
20
+ env: {
21
+ GITHUB_TOKEN: '<your-github-token>',
22
+ },
23
+ },
24
+ postgres: {
25
+ name: 'postgres',
26
+ description: 'PostgreSQL database operations',
27
+ command: 'npx',
28
+ args: ['-y', '@modelcontextprotocol/server-postgres'],
29
+ env: {
30
+ POSTGRES_CONNECTION_STRING: 'postgresql://user:password@localhost:5432/dbname',
31
+ },
32
+ },
33
+ sqlite: {
34
+ name: 'sqlite',
35
+ description: 'SQLite database operations',
36
+ command: 'npx',
37
+ args: ['-y', '@modelcontextprotocol/server-sqlite', '--db-path', './database.db'],
38
+ env: {},
39
+ },
40
+ 'brave-search': {
41
+ name: 'brave-search',
42
+ description: 'Web search using Brave Search API',
43
+ command: 'npx',
44
+ args: ['-y', '@modelcontextprotocol/server-brave-search'],
45
+ env: {
46
+ BRAVE_API_KEY: '<your-brave-api-key>',
47
+ },
48
+ },
49
+ };
50
+
51
+ // Get MCP servers from config
52
+ function getMcpServers() {
53
+ const config = loadConfig();
54
+ return config?.mcpServers || {};
55
+ }
56
+
57
+ // Save MCP servers to config
58
+ function saveMcpServers(servers) {
59
+ const config = loadConfig() || {};
60
+ config.mcpServers = servers;
61
+ saveConfig(config);
62
+ }
63
+
64
+ // Add MCP server
65
+ function addMcpServer(name, template = null, customConfig = null) {
66
+ const servers = getMcpServers();
67
+
68
+ if (servers[name]) {
69
+ throw new Error(`MCP sunucu zaten mevcut: ${name}`);
70
+ }
71
+
72
+ let serverConfig;
73
+
74
+ if (template && MCP_TEMPLATES[template]) {
75
+ serverConfig = { ...MCP_TEMPLATES[template] };
76
+ delete serverConfig.name;
77
+ } else if (customConfig) {
78
+ serverConfig = customConfig;
79
+ } else {
80
+ throw new Error('Template veya custom config gerekli');
81
+ }
82
+
83
+ servers[name] = {
84
+ ...serverConfig,
85
+ disabled: false,
86
+ autoApprove: [],
87
+ };
88
+
89
+ saveMcpServers(servers);
90
+ }
91
+
92
+ // Remove MCP server
93
+ function removeMcpServer(name) {
94
+ const servers = getMcpServers();
95
+
96
+ if (!servers[name]) {
97
+ throw new Error(`MCP sunucu bulunamadı: ${name}`);
98
+ }
99
+
100
+ delete servers[name];
101
+ saveMcpServers(servers);
102
+ }
103
+
104
+ // Test MCP server connection
105
+ function testMcpServer(name) {
106
+ const servers = getMcpServers();
107
+
108
+ if (!servers[name]) {
109
+ throw new Error(`MCP sunucu bulunamadı: ${name}`);
110
+ }
111
+
112
+ const server = servers[name];
113
+
114
+ // Check if command exists
115
+ try {
116
+ if (server.command === 'npx') {
117
+ // Check if npx is available
118
+ execSync('npx --version', { stdio: 'ignore' });
119
+ } else {
120
+ // Check if custom command exists
121
+ execSync(`${server.command} --version`, { stdio: 'ignore' });
122
+ }
123
+ } catch {
124
+ throw new Error(`Komut bulunamadı: ${server.command}`);
125
+ }
126
+
127
+ // Check environment variables
128
+ if (server.env) {
129
+ const missingEnvVars = [];
130
+ for (const [key, value] of Object.entries(server.env)) {
131
+ if (value.startsWith('<') && value.endsWith('>')) {
132
+ missingEnvVars.push(key);
133
+ } else if (!process.env[key] && value) {
134
+ missingEnvVars.push(key);
135
+ }
136
+ }
137
+
138
+ if (missingEnvVars.length > 0) {
139
+ throw new Error(`Eksik environment variable'lar: ${missingEnvVars.join(', ')}`);
140
+ }
141
+ }
142
+
143
+ return true;
144
+ }
145
+
146
+ // Enable/disable MCP server
147
+ function toggleMcpServer(name, enabled) {
148
+ const servers = getMcpServers();
149
+
150
+ if (!servers[name]) {
151
+ throw new Error(`MCP sunucu bulunamadı: ${name}`);
152
+ }
153
+
154
+ servers[name].disabled = !enabled;
155
+ saveMcpServers(servers);
156
+ }
157
+
158
+ // Get MCP templates
159
+ function getMcpTemplates() {
160
+ return MCP_TEMPLATES;
161
+ }
162
+
163
+ // Update MCP server config
164
+ function updateMcpServer(name, updates) {
165
+ const servers = getMcpServers();
166
+
167
+ if (!servers[name]) {
168
+ throw new Error(`MCP sunucu bulunamadı: ${name}`);
169
+ }
170
+
171
+ servers[name] = {
172
+ ...servers[name],
173
+ ...updates,
174
+ };
175
+
176
+ saveMcpServers(servers);
177
+ }
178
+
179
+ module.exports = {
180
+ getMcpServers,
181
+ saveMcpServers,
182
+ addMcpServer,
183
+ removeMcpServer,
184
+ testMcpServer,
185
+ toggleMcpServer,
186
+ getMcpTemplates,
187
+ updateMcpServer,
188
+ };
@@ -0,0 +1,285 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const { execSync } = require('child_process');
5
+ const { CONFIG_DIR } = require('./config');
6
+
7
+ const BUILTIN_SKILLS_DIR = path.join(__dirname, '..', '..', 'skills');
8
+ const USER_SKILLS_DIR = path.join(CONFIG_DIR, 'skills');
9
+ const PROJECT_SKILLS_DIR = path.join(process.cwd(), '.natureco', 'skills');
10
+
11
+ // Skill metadata parser
12
+ function parseSkillMetadata(content) {
13
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
14
+ if (!frontmatterMatch) return null;
15
+
16
+ const frontmatter = frontmatterMatch[1];
17
+ const metadata = {};
18
+
19
+ // name
20
+ const nameMatch = frontmatter.match(/name:\s*(.+)/);
21
+ if (nameMatch) metadata.name = nameMatch[1].trim();
22
+
23
+ // description
24
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
25
+ if (descMatch) metadata.description = descMatch[1].trim();
26
+
27
+ // metadata JSON
28
+ const metaMatch = frontmatter.match(/metadata:\s*(\{[\s\S]*?\})/);
29
+ if (metaMatch) {
30
+ try {
31
+ metadata.metadata = JSON.parse(metaMatch[1]);
32
+ } catch {
33
+ metadata.metadata = {};
34
+ }
35
+ }
36
+
37
+ return metadata;
38
+ }
39
+
40
+ // Skill gating - requirements check
41
+ function checkSkillRequirements(metadata) {
42
+ const errors = [];
43
+
44
+ if (metadata?.metadata?.natureco?.requires?.bins) {
45
+ const bins = metadata.metadata.natureco.requires.bins;
46
+ for (const bin of bins) {
47
+ try {
48
+ execSync(`${bin} --version`, { stdio: 'ignore' });
49
+ } catch {
50
+ errors.push(`Binary gerekli: ${bin}`);
51
+ }
52
+ }
53
+ }
54
+
55
+ if (metadata?.metadata?.natureco?.requires?.env) {
56
+ const envVars = metadata.metadata.natureco.requires.env;
57
+ for (const envVar of envVars) {
58
+ if (!process.env[envVar]) {
59
+ errors.push(`Environment variable gerekli: ${envVar}`);
60
+ }
61
+ }
62
+ }
63
+
64
+ if (metadata?.metadata?.natureco?.os) {
65
+ const supportedOS = metadata.metadata.natureco.os;
66
+ if (!supportedOS.includes(os.platform())) {
67
+ errors.push(`Platform desteklenmiyor. Desteklenen: ${supportedOS.join(', ')}`);
68
+ }
69
+ }
70
+
71
+ return errors;
72
+ }
73
+
74
+ // Get all skills from all sources
75
+ function getSkills() {
76
+ const skills = [];
77
+
78
+ // Builtin skills
79
+ if (fs.existsSync(BUILTIN_SKILLS_DIR)) {
80
+ const builtinDirs = fs.readdirSync(BUILTIN_SKILLS_DIR, { withFileTypes: true })
81
+ .filter(d => d.isDirectory());
82
+
83
+ for (const dir of builtinDirs) {
84
+ const skillFile = path.join(BUILTIN_SKILLS_DIR, dir.name, 'SKILL.md');
85
+ if (fs.existsSync(skillFile)) {
86
+ const content = fs.readFileSync(skillFile, 'utf8');
87
+ const metadata = parseSkillMetadata(content);
88
+ if (metadata) {
89
+ skills.push({
90
+ ...metadata,
91
+ slug: dir.name,
92
+ source: 'builtin',
93
+ path: path.join(BUILTIN_SKILLS_DIR, dir.name),
94
+ });
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ // User skills
101
+ if (fs.existsSync(USER_SKILLS_DIR)) {
102
+ const userDirs = fs.readdirSync(USER_SKILLS_DIR, { withFileTypes: true })
103
+ .filter(d => d.isDirectory());
104
+
105
+ for (const dir of userDirs) {
106
+ const skillFile = path.join(USER_SKILLS_DIR, dir.name, 'SKILL.md');
107
+ if (fs.existsSync(skillFile)) {
108
+ const content = fs.readFileSync(skillFile, 'utf8');
109
+ const metadata = parseSkillMetadata(content);
110
+ if (metadata) {
111
+ skills.push({
112
+ ...metadata,
113
+ slug: dir.name,
114
+ source: 'user',
115
+ path: path.join(USER_SKILLS_DIR, dir.name),
116
+ });
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ // Project skills
123
+ if (fs.existsSync(PROJECT_SKILLS_DIR)) {
124
+ const projectDirs = fs.readdirSync(PROJECT_SKILLS_DIR, { withFileTypes: true })
125
+ .filter(d => d.isDirectory());
126
+
127
+ for (const dir of projectDirs) {
128
+ const skillFile = path.join(PROJECT_SKILLS_DIR, dir.name, 'SKILL.md');
129
+ if (fs.existsSync(skillFile)) {
130
+ const content = fs.readFileSync(skillFile, 'utf8');
131
+ const metadata = parseSkillMetadata(content);
132
+ if (metadata) {
133
+ skills.push({
134
+ ...metadata,
135
+ slug: dir.name,
136
+ source: 'project',
137
+ path: path.join(PROJECT_SKILLS_DIR, dir.name),
138
+ });
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ return skills;
145
+ }
146
+
147
+ // Install skill from NatureHub
148
+ async function installSkill(slug) {
149
+ const url = `https://natureco.me/hub/skills/${slug}/SKILL.md`;
150
+
151
+ try {
152
+ const response = await fetch(url);
153
+ if (!response.ok) {
154
+ throw new Error(`Skill bulunamadı: ${slug}`);
155
+ }
156
+
157
+ const content = await response.text();
158
+ const metadata = parseSkillMetadata(content);
159
+
160
+ if (!metadata) {
161
+ throw new Error('Geçersiz skill formatı');
162
+ }
163
+
164
+ // Check requirements
165
+ const errors = checkSkillRequirements(metadata);
166
+ if (errors.length > 0) {
167
+ throw new Error(`Gereksinimler karşılanmadı:\n${errors.join('\n')}`);
168
+ }
169
+
170
+ // Save to user skills
171
+ if (!fs.existsSync(USER_SKILLS_DIR)) {
172
+ fs.mkdirSync(USER_SKILLS_DIR, { recursive: true });
173
+ }
174
+
175
+ const skillDir = path.join(USER_SKILLS_DIR, slug);
176
+ if (!fs.existsSync(skillDir)) {
177
+ fs.mkdirSync(skillDir, { recursive: true });
178
+ }
179
+
180
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), content, 'utf8');
181
+ } catch (err) {
182
+ throw new Error(`Skill yüklenemedi: ${err.message}`);
183
+ }
184
+ }
185
+
186
+ // Remove skill
187
+ function removeSkill(slug) {
188
+ const skillPath = path.join(USER_SKILLS_DIR, slug);
189
+
190
+ if (!fs.existsSync(skillPath)) {
191
+ throw new Error(`Skill bulunamadı: ${slug}`);
192
+ }
193
+
194
+ fs.rmSync(skillPath, { recursive: true, force: true });
195
+ }
196
+
197
+ // Update all skills
198
+ async function updateAllSkills() {
199
+ const skills = getSkills().filter(s => s.source === 'user');
200
+ const updated = [];
201
+
202
+ for (const skill of skills) {
203
+ try {
204
+ await installSkill(skill.slug);
205
+ updated.push(skill.slug);
206
+ } catch {
207
+ // Skip failed updates
208
+ }
209
+ }
210
+
211
+ return updated;
212
+ }
213
+
214
+ // Create skill template
215
+ function createSkillTemplate(name) {
216
+ const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
217
+ const skillDir = path.join(USER_SKILLS_DIR, slug);
218
+
219
+ if (fs.existsSync(skillDir)) {
220
+ throw new Error(`Skill zaten mevcut: ${slug}`);
221
+ }
222
+
223
+ if (!fs.existsSync(USER_SKILLS_DIR)) {
224
+ fs.mkdirSync(USER_SKILLS_DIR, { recursive: true });
225
+ }
226
+
227
+ fs.mkdirSync(skillDir, { recursive: true });
228
+
229
+ const template = `---
230
+ name: ${name}
231
+ description: ${name} skill açıklaması
232
+ metadata: {"natureco": {"requires": {"bins": [], "env": []}, "os": ["win32","darwin","linux"]}}
233
+ ---
234
+
235
+ # ${name} Skill
236
+
237
+ Bu skill ${name} işlemlerini yapar.
238
+
239
+ ## Kullanım
240
+
241
+ [Kullanım talimatları]
242
+
243
+ ## Örnekler
244
+
245
+ \`\`\`
246
+ [Örnek komutlar]
247
+ \`\`\`
248
+
249
+ ## Notlar
250
+
251
+ [Ek notlar]
252
+ `;
253
+
254
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), template, 'utf8');
255
+
256
+ return path.join(skillDir, 'SKILL.md');
257
+ }
258
+
259
+ // Get skill prompt injection content
260
+ function getSkillPrompts() {
261
+ const skills = getSkills();
262
+ const prompts = [];
263
+
264
+ for (const skill of skills) {
265
+ const skillFile = path.join(skill.path, 'SKILL.md');
266
+ if (fs.existsSync(skillFile)) {
267
+ const content = fs.readFileSync(skillFile, 'utf8');
268
+ // Remove frontmatter
269
+ const withoutFrontmatter = content.replace(/^---\n[\s\S]*?\n---\n/, '');
270
+ prompts.push(`\n## Skill: ${skill.name}\n${withoutFrontmatter}`);
271
+ }
272
+ }
273
+
274
+ return prompts.join('\n\n');
275
+ }
276
+
277
+ module.exports = {
278
+ getSkills,
279
+ installSkill,
280
+ removeSkill,
281
+ updateAllSkills,
282
+ createSkillTemplate,
283
+ getSkillPrompts,
284
+ checkSkillRequirements,
285
+ };