natureco-cli 2.23.29 → 2.23.31

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 (111) hide show
  1. package/README.md +94 -11
  2. package/bin/natureco.js +495 -94
  3. package/package.json +1 -1
  4. package/src/commands/acp.js +39 -0
  5. package/src/commands/admin-rpc.js +302 -0
  6. package/src/commands/agent.js +280 -0
  7. package/src/commands/agents.js +114 -30
  8. package/src/commands/approvals.js +214 -0
  9. package/src/commands/backup.js +124 -0
  10. package/src/commands/bonjour.js +167 -0
  11. package/src/commands/browser.js +815 -0
  12. package/src/commands/capability.js +237 -0
  13. package/src/commands/channels.js +422 -267
  14. package/src/commands/chat.js +5 -8
  15. package/src/commands/clawbot.js +19 -0
  16. package/src/commands/clickclack.js +130 -0
  17. package/src/commands/code.js +3 -2
  18. package/src/commands/commitments.js +148 -0
  19. package/src/commands/completion.js +84 -0
  20. package/src/commands/config.js +219 -30
  21. package/src/commands/configure.js +110 -0
  22. package/src/commands/crestodian.js +92 -0
  23. package/src/commands/cron.js +239 -19
  24. package/src/commands/daemon.js +90 -0
  25. package/src/commands/dashboard.js +47 -374
  26. package/src/commands/device-pair.js +248 -0
  27. package/src/commands/devices.js +137 -0
  28. package/src/commands/directory.js +179 -0
  29. package/src/commands/dns.js +196 -0
  30. package/src/commands/docs.js +136 -0
  31. package/src/commands/doctor.js +143 -492
  32. package/src/commands/exec-policy.js +80 -0
  33. package/src/commands/gateway-server.js +1155 -24
  34. package/src/commands/gateway.js +492 -249
  35. package/src/commands/health.js +148 -0
  36. package/src/commands/help.js +24 -25
  37. package/src/commands/hooks.js +141 -87
  38. package/src/commands/imessage.js +128 -14
  39. package/src/commands/infer.js +1474 -0
  40. package/src/commands/irc.js +64 -15
  41. package/src/commands/logs.js +122 -99
  42. package/src/commands/mattermost.js +114 -12
  43. package/src/commands/mcp.js +121 -309
  44. package/src/commands/memory-cmd.js +134 -1
  45. package/src/commands/memory.js +128 -0
  46. package/src/commands/message.js +720 -134
  47. package/src/commands/migrate.js +213 -2
  48. package/src/commands/models.js +39 -1
  49. package/src/commands/node.js +98 -0
  50. package/src/commands/nodes.js +362 -0
  51. package/src/commands/oc-path.js +200 -0
  52. package/src/commands/onboard.js +129 -0
  53. package/src/commands/open-prose.js +67 -0
  54. package/src/commands/pairing.js +108 -107
  55. package/src/commands/path.js +206 -0
  56. package/src/commands/plugins.js +35 -1
  57. package/src/commands/policy.js +176 -0
  58. package/src/commands/proxy.js +306 -0
  59. package/src/commands/qr.js +70 -0
  60. package/src/commands/reset.js +101 -94
  61. package/src/commands/sandbox.js +125 -0
  62. package/src/commands/secrets.js +201 -0
  63. package/src/commands/sessions.js +110 -51
  64. package/src/commands/setup.js +102 -543
  65. package/src/commands/signal.js +447 -18
  66. package/src/commands/skills.js +67 -1
  67. package/src/commands/sms.js +123 -19
  68. package/src/commands/status.js +101 -127
  69. package/src/commands/system.js +53 -0
  70. package/src/commands/tasks.js +208 -100
  71. package/src/commands/terminal.js +139 -0
  72. package/src/commands/thread-ownership.js +157 -0
  73. package/src/commands/transcripts.js +95 -0
  74. package/src/commands/tui.js +41 -0
  75. package/src/commands/uninstall.js +73 -92
  76. package/src/commands/update.js +146 -91
  77. package/src/commands/voice.js +82 -0
  78. package/src/commands/vydra.js +98 -0
  79. package/src/commands/webhooks.js +58 -66
  80. package/src/commands/wiki.js +783 -0
  81. package/src/commands/workboard.js +207 -0
  82. package/src/tools/audio_understanding.js +154 -0
  83. package/src/tools/browser.js +112 -0
  84. package/src/tools/canvas.js +104 -0
  85. package/src/tools/document_extract.js +84 -0
  86. package/src/tools/duckduckgo.js +54 -0
  87. package/src/tools/exa_search.js +66 -0
  88. package/src/tools/firecrawl.js +104 -0
  89. package/src/tools/image_generation.js +99 -0
  90. package/src/tools/llm_task.js +118 -0
  91. package/src/tools/media_understanding.js +128 -0
  92. package/src/tools/music_generation.js +113 -0
  93. package/src/tools/parallel_search.js +77 -0
  94. package/src/tools/phone_control.js +80 -0
  95. package/src/tools/phone_control_enhanced.js +184 -0
  96. package/src/tools/searxng.js +61 -0
  97. package/src/tools/speech_to_text.js +135 -0
  98. package/src/tools/text_to_speech.js +105 -0
  99. package/src/tools/thread_ownership.js +88 -0
  100. package/src/tools/video_generation.js +72 -0
  101. package/src/tools/web_readability.js +104 -0
  102. package/src/utils/agents-md.js +85 -0
  103. package/src/utils/api.js +39 -40
  104. package/src/utils/format.js +144 -0
  105. package/src/utils/headless.js +2 -1
  106. package/src/utils/memory.js +200 -0
  107. package/src/utils/parallel-tools.js +106 -0
  108. package/src/utils/sub-agent.js +148 -0
  109. package/src/utils/token-budget.js +304 -0
  110. package/src/utils/tool-runner.js +7 -5
  111. package/src/utils/web-fetch.js +107 -0
@@ -1,26 +1,32 @@
1
1
  const chalk = require('chalk');
2
+ const F = require('../utils/format');
2
3
  const path = require('path');
3
- const { getConfig, setConfigValue, getAllConfig, listBackups, restoreConfig, CONFIG_BACKUP_DIR } = require('../utils/config');
4
+ const fs = require('fs');
5
+ const { getConfig, setConfigValue, getAllConfig, listBackups, restoreConfig, saveConfig, CONFIG_FILE, CONFIG_BACKUP_DIR } = require('../utils/config');
6
+ const TB = require('../utils/token-budget');
4
7
 
5
8
  function config(args) {
6
9
  const [action, key, ...valueParts] = args;
7
10
 
8
11
  if (!action) {
9
- console.log(chalk.red('\n❌ Kullanım: natureco config <get|set|list|backups|restore> [key] [value]\n'));
12
+ F.error('Kullanım: natureco config <get|set|unset|list|file|schema|validate|backups|restore> [key] [value]');
10
13
  process.exit(1);
11
14
  }
12
15
 
13
16
  if (action === 'list') {
14
17
  const cfg = getAllConfig();
15
- console.log(chalk.green.bold('\n╭─ Config ─╮\n'));
16
- console.log(JSON.stringify(cfg, null, 2));
17
- console.log('');
18
+ F.header('Configuration');
19
+ const rows = Object.entries(cfg).map(([k, v]) => [
20
+ k,
21
+ typeof v === 'string' ? v : JSON.stringify(v),
22
+ ]);
23
+ F.table(['Key', 'Value'], rows);
18
24
  return;
19
25
  }
20
26
 
21
27
  if (action === 'get') {
22
28
  if (!key) {
23
- console.log(chalk.red('\n❌ Key belirtilmedi.\n'));
29
+ F.error('Key belirtilmedi.');
24
30
  process.exit(1);
25
31
  }
26
32
  const cfg = getConfig();
@@ -30,21 +36,20 @@ function config(args) {
30
36
  value = value?.[k];
31
37
  }
32
38
  if (value === undefined) {
33
- console.log(chalk.gray(`\n${key}: (tanımlı değil)\n`));
39
+ F.info(`${key}: (tanımlı değil)`);
34
40
  } else {
35
- console.log(chalk.cyan(`\n${key}:`), chalk.white(JSON.stringify(value, null, 2)));
36
- console.log('');
41
+ F.kv(key, JSON.stringify(value, null, 2));
37
42
  }
38
43
  return;
39
44
  }
40
45
 
41
46
  if (action === 'set') {
42
47
  if (!key) {
43
- console.log(chalk.red('\n❌ Key belirtilmedi.\n'));
48
+ F.error('Key belirtilmedi.');
44
49
  process.exit(1);
45
50
  }
46
51
  if (valueParts.length === 0) {
47
- console.log(chalk.red('\n❌ Value belirtilmedi.\n'));
52
+ F.error('Value belirtilmedi.');
48
53
  process.exit(1);
49
54
  }
50
55
  const value = valueParts.join(' ');
@@ -55,24 +60,121 @@ function config(args) {
55
60
  } catch {}
56
61
 
57
62
  setConfigValue(key, parsedValue);
58
- console.log(chalk.green(`\n✅ ${key} = ${JSON.stringify(parsedValue)}\n`));
63
+ F.success(`${key} = ${JSON.stringify(parsedValue)}`);
64
+ return;
65
+ }
66
+
67
+ if (action === 'unset') {
68
+ if (!key) {
69
+ F.error('Key belirtilmedi.');
70
+ process.exit(1);
71
+ }
72
+ const cfg = getConfig();
73
+ const keys = key.split('.');
74
+ let obj = cfg;
75
+ for (let i = 0; i < keys.length - 1; i++) {
76
+ obj = obj?.[keys[i]];
77
+ }
78
+ if (obj && keys[keys.length - 1] in obj) {
79
+ delete obj[keys[keys.length - 1]];
80
+ }
81
+ saveConfig(cfg);
82
+ F.success(`Unset: ${key}`);
83
+ return;
84
+ }
85
+
86
+ if (action === 'file') {
87
+ const configPath = CONFIG_FILE;
88
+ const exists = fs.existsSync(configPath);
89
+ F.header('Config File');
90
+ F.kv('Path', configPath);
91
+ if (exists) {
92
+ const stats = fs.statSync(configPath);
93
+ const sizeKB = (stats.size / 1024).toFixed(2);
94
+ F.kv('Size', `${sizeKB} KB`);
95
+ F.kv('Last Modified', String(stats.mtime));
96
+ const content = fs.readFileSync(configPath, 'utf8');
97
+ try {
98
+ const parsed = JSON.parse(content);
99
+ const keys = Object.keys(parsed);
100
+ F.kv('Keys', keys.length ? keys.join(', ') : '(empty)');
101
+ } catch {
102
+ F.error('(invalid JSON)');
103
+ }
104
+ } else {
105
+ F.warning('(file does not exist)');
106
+ }
107
+ return;
108
+ }
109
+
110
+ if (action === 'schema') {
111
+ F.section('Config Schema');
112
+ const schema = {
113
+ apiKey: { type: 'string', description: 'API key for the provider', required: true },
114
+ providerUrl: { type: 'string', description: 'Base URL of the provider API', required: true },
115
+ providerModel: { type: 'string', description: 'Default model identifier', required: false, default: 'gpt-4' },
116
+ temperature: { type: 'number', description: 'Sampling temperature (0-2)', required: false, default: 0.7 },
117
+ maxTokens: { type: 'number', description: 'Maximum tokens per response', required: false, default: 2048 },
118
+ systemPrompt: { type: 'string', description: 'Custom system prompt', required: false },
119
+ proxy: { type: 'object', description: 'Proxy configuration', required: false, properties: { host: { type: 'string' }, port: { type: 'number' } } },
120
+ timeout: { type: 'number', description: 'Request timeout in milliseconds', required: false, default: 30000 },
121
+ organization: { type: 'string', description: 'Organization ID for multi-tenant setups', required: false },
122
+ };
123
+ const rows = Object.entries(schema).map(([k, v]) => [
124
+ v.required ? `${k}*` : k,
125
+ v.type,
126
+ v.description,
127
+ v.default !== undefined ? JSON.stringify(v.default) : '—',
128
+ ]);
129
+ F.table(['Field', 'Type', 'Description', 'Default'], rows);
130
+ F.meta('* = required');
131
+ return;
132
+ }
133
+
134
+ if (action === 'validate') {
135
+ const cfg = getConfig();
136
+ F.section('Config Validation');
137
+ const checks = [
138
+ { field: 'apiKey', label: 'API Key', required: true },
139
+ { field: 'providerUrl', label: 'Provider URL', required: true },
140
+ { field: 'providerModel', label: 'Provider Model', required: false },
141
+ ];
142
+ let allPass = true;
143
+ for (const check of checks) {
144
+ const value = cfg[check.field];
145
+ if (check.required && (!value || typeof value !== 'string')) {
146
+ F.error(`${check.label}: FAIL (required)`);
147
+ allPass = false;
148
+ } else if (value && typeof value !== 'string') {
149
+ F.warning(`${check.label}: WARN (expected string, got ${typeof value})`);
150
+ } else if (value) {
151
+ F.success(`${check.label}: PASS`);
152
+ } else {
153
+ F.info(`${check.label}: not set`);
154
+ }
155
+ }
156
+ if (allPass) {
157
+ F.success('All required fields are set.');
158
+ } else {
159
+ F.error('Some required fields are missing.');
160
+ }
59
161
  return;
60
162
  }
61
163
 
62
164
  if (action === 'backups' || action === 'backup') {
63
165
  const backups = listBackups();
64
- console.log(chalk.cyan.bold('\n Config Yedekleri\n'));
166
+ F.section('Config Yedekleri');
65
167
  if (backups.length === 0) {
66
- console.log(chalk.gray(' Henüz yedek alınmamış.\n'));
168
+ F.info('Hen\u00fcz yedek al\u0131nmam\u0131\u015f.');
67
169
  return;
68
170
  }
69
- backups.forEach((f, i) => {
171
+ const rows = backups.map((f, i) => {
70
172
  const ts = f.replace(/^config-|\.json$/g, '').replace(/T/, ' ').replace(/-/g, ':').replace(/:[^:]*$/, '');
71
- console.log(chalk.white(` ${i + 1}. ${ts}`));
72
- console.log(chalk.gray(` ${path.join(CONFIG_BACKUP_DIR, f)}`));
173
+ return [String(i + 1), ts, path.join(CONFIG_BACKUP_DIR, f)];
73
174
  });
74
- console.log(chalk.gray(`\n Geri yüklemek için: natureco config restore <dosya-adı>`));
75
- console.log(chalk.gray(` Örnek: natureco config restore ${backups[0] || 'config-....json'}\n`));
175
+ F.table(['#', 'Tarih', 'Dosya'], rows);
176
+ F.meta('Geri y\u00fcklemek i\u00e7in: natureco config restore <dosya-ad\u0131>');
177
+ F.meta(`\u00d6rnek: natureco config restore ${backups[0] || 'config-....json'}`);
76
178
  return;
77
179
  }
78
180
 
@@ -81,31 +183,118 @@ function config(args) {
81
183
  if (!backupId) {
82
184
  const backups = listBackups();
83
185
  if (backups.length === 0) {
84
- console.log(chalk.red('\n❌ Geri yüklenecek yedek bulunamadı.\n'));
186
+ F.error('Geri y\u00fcklenecek yedek bulunamad\u0131.');
85
187
  process.exit(1);
86
188
  }
87
- console.log(chalk.cyan.bold('\n Geri Yükleme\n'));
88
- console.log(chalk.gray(' En son yedek: ') + chalk.white(backups[0]));
89
- console.log('');
90
- console.log(chalk.gray(' Kullanım: ') + chalk.cyan(`natureco config restore ${backups[0]}`));
91
- console.log(chalk.gray(' Yedekleri listelemek için: ') + chalk.cyan('natureco config backups\n'));
189
+ F.section('Geri Y\u00fckleme');
190
+ F.kv('En son yedek', backups[0]);
191
+ F.meta(`Kullan\u0131m: natureco config restore ${backups[0]}`);
192
+ F.meta('Yedekleri listelemek i\u00e7in: natureco config backups');
92
193
  return;
93
194
  }
94
195
 
95
196
  try {
96
197
  const result = restoreConfig(backupId);
97
- console.log(chalk.green(`\n✅ Config geri yüklendi: ${result.timestamp}\n`));
98
- console.log(chalk.gray(` Kaynak: ${result.path}\n`));
198
+ F.success(`Config geri y\u00fcklendi: ${result.timestamp}`);
199
+ F.meta(`Kaynak: ${result.path}`);
99
200
  } catch (err) {
100
- console.log(chalk.red(`\n❌ Geri yükleme başarısız: ${err.message}\n`));
201
+ F.error(`Geri y\u00fckleme ba\u015far\u0131s\u0131z: ${err.message}`);
101
202
  process.exit(1);
102
203
  }
103
204
  return;
104
205
  }
105
206
 
106
- console.log(chalk.red(`\n❌ Geçersiz action: ${action}\n`));
107
- console.log(chalk.gray(' Kullanım: natureco config <get|set|list|backups|restore> [key] [value]\n'));
207
+ if (action === 'budget') {
208
+ return configBudget(key, valueParts);
209
+ }
210
+
211
+ F.error(`Ge\u00e7ersiz action: ${action}`);
212
+ F.meta('Kullan\u0131m: natureco config <get|set|unset|list|file|schema|validate|backups|restore|budget> [key] [value]');
108
213
  process.exit(1);
109
214
  }
110
215
 
216
+ function configBudget(sub, args) {
217
+ if (!sub || sub === 'show') {
218
+ const b = TB.load();
219
+ F.header('Token Budget');
220
+ F.kv('Preset', b.preset || 'balanced');
221
+ F.kv('Max Context Tokens', String(b.maxContextTokens));
222
+ F.kv('Preserve Recent Tokens', String(b.preserveRecentTokens));
223
+ F.kv('Tail Turns', String(b.tailTurns));
224
+ F.divider();
225
+ F.section('Output Limits');
226
+ F.kv('Tool Max Lines', String(b.toolMaxLines));
227
+ F.kv('Tool Max Chars', String(b.toolMaxChars));
228
+ F.kv('MCP Desc Max Chars', String(b.mcpDescMaxChars));
229
+ F.divider();
230
+ F.section('Content Truncation');
231
+ F.kv('System Prompt Max', String(b.systemPromptMaxChars));
232
+ F.kv('Memory Max Facts', String(b.memoryMaxFacts));
233
+ F.kv('Project Memory Max', String(b.projectMemoryMaxChars));
234
+ F.kv('File Content Max', String(b.fileContentMaxChars));
235
+ F.divider();
236
+ F.section('Conversation');
237
+ F.kv('Messages on Disk', String(b.conversationOnDisk));
238
+ F.kv('Messages in Context', String(b.conversationInContext));
239
+ F.kv('Auto Compact', String(b.autoCompact));
240
+ F.kv('Reserved Tokens', String(b.reservedTokens));
241
+ F.meta('');
242
+ F.meta('Switch preset: natureco config budget preset <efficient|balanced|quality>');
243
+ F.meta('Set a value: natureco config budget set <key> <value>');
244
+ return;
245
+ }
246
+
247
+ if (sub === 'preset') {
248
+ const name = args[0];
249
+ if (!name || !['efficient', 'balanced', 'quality'].includes(name)) {
250
+ F.error('Preset: efficient, balanced, quality');
251
+ F.table(['Preset', 'Description', 'Context'], TB.getPresets().map(p => [p.key, p.label, String(p.maxContextTokens)]));
252
+ return;
253
+ }
254
+ TB.setPreset(name);
255
+ F.success('Token budget preset: ' + name);
256
+ return;
257
+ }
258
+
259
+ if (sub === 'set') {
260
+ const key = args[0];
261
+ const val = args[1];
262
+ if (!key || val === undefined) {
263
+ F.error('Kullan\u0131m: natureco config budget set <key> <value>');
264
+ return;
265
+ }
266
+ const budget = TB.load();
267
+ if (!(key in budget)) {
268
+ F.error('Bilinmeyen budget key: ' + key);
269
+ return;
270
+ }
271
+ const num = Number(val);
272
+ budget[key] = isNaN(num) ? val : num;
273
+ TB.save(budget);
274
+ F.success(key + ' = ' + budget[key]);
275
+ return;
276
+ }
277
+
278
+ if (sub === 'usage') {
279
+ const usage = TB.getAllUsage();
280
+ if (!usage || Object.keys(usage).length === 0) {
281
+ F.info('Hen\u00fcz token kullan\u0131m\u0131 kaydedilmedi.');
282
+ return;
283
+ }
284
+ F.header('Token Usage');
285
+ const rows = Object.entries(usage).map(([sid, u]) => [
286
+ sid.slice(0, 20),
287
+ String(u.total || 0),
288
+ String(u.input || 0),
289
+ String(u.output || 0),
290
+ String(u.count || 0)
291
+ ]);
292
+ F.table(['Session', 'Total', 'Input', 'Output', 'Calls'], rows);
293
+ return;
294
+ }
295
+
296
+ F.error('Budget alt komutu: ' + sub);
297
+ F.meta('Kullan\u0131m: natureco config budget <show|preset|set|usage>');
298
+ }
299
+
111
300
  module.exports = config;
@@ -0,0 +1,110 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const readline = require('readline');
6
+
7
+ const BASE_DIR = path.join(os.homedir(), '.natureco');
8
+ const CONFIG_FILE = path.join(BASE_DIR, 'config.json');
9
+
10
+ function getConfig() {
11
+ if (!fs.existsSync(CONFIG_FILE)) return {};
12
+ try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); }
13
+ catch { return {}; }
14
+ }
15
+
16
+ function saveConfig(data) {
17
+ if (!fs.existsSync(BASE_DIR)) fs.mkdirSync(BASE_DIR, { recursive: true });
18
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2), 'utf8');
19
+ }
20
+
21
+ function rlQuestion(query) {
22
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
23
+ return new Promise(resolve => {
24
+ rl.question(query, answer => { rl.close(); resolve(answer.trim()); });
25
+ });
26
+ }
27
+
28
+ async function configure(params) {
29
+ try {
30
+ const [action] = params || [];
31
+
32
+ if (!action) return cmdMenu();
33
+ if (action === 'gateway') return await cmdGateway();
34
+ if (action === 'auth') return await cmdAuth();
35
+ if (action === 'channels') return cmdChannels();
36
+ if (action === 'plugins') return cmdPlugins();
37
+ if (action === 'skills') return cmdSkills();
38
+
39
+ console.log(chalk.red(`\n Unknown configure action: ${action}\n`));
40
+ console.log(chalk.gray(' Usage: natureco configure [gateway|auth|channels|plugins|skills]\n'));
41
+ } catch (err) {
42
+ console.log(chalk.red(`\n Configure error: ${err.message}\n`));
43
+ }
44
+ }
45
+
46
+ function cmdMenu() {
47
+ console.log(chalk.cyan('\n Configure\n'));
48
+ console.log(chalk.gray(' Run with an action:'));
49
+ console.log(chalk.gray(' natureco configure gateway Update gateway config'));
50
+ console.log(chalk.gray(' natureco configure auth Update provider keys'));
51
+ console.log(chalk.gray(' natureco configure channels Re-run channels setup'));
52
+ console.log(chalk.gray(' natureco configure plugins Re-run plugin setup'));
53
+ console.log(chalk.gray(' natureco configure skills Re-run skills setup\n'));
54
+ }
55
+
56
+ async function cmdGateway() {
57
+ const cfg = getConfig();
58
+ console.log(chalk.cyan('\n Gateway Configuration\n'));
59
+ console.log(chalk.gray(' Current: ') + chalk.white(cfg.gatewayUrl || 'not set'));
60
+
61
+ const url = await rlQuestion(` Gateway URL (${cfg.gatewayUrl || 'ws://localhost:3848'}): `);
62
+ const port = await rlQuestion(` Port (${cfg.gatewayPort || 3848}): `);
63
+
64
+ cfg.gatewayUrl = url || cfg.gatewayUrl || 'ws://localhost:3848';
65
+ cfg.gatewayPort = parseInt(port) || cfg.gatewayPort || 3848;
66
+ saveConfig(cfg);
67
+
68
+ console.log(chalk.green(`\n Gateway updated: ${cfg.gatewayUrl}:${cfg.gatewayPort}\n`));
69
+ }
70
+
71
+ async function cmdAuth() {
72
+ const cfg = getConfig();
73
+ console.log(chalk.cyan('\n Auth Configuration\n'));
74
+ console.log(chalk.gray(' Current provider key: ') + chalk.white(cfg.providerApiKey ? '(set)' : '(not set)'));
75
+
76
+ const key = await rlQuestion(' Provider API Key (leave blank to keep current): ');
77
+ if (key) {
78
+ cfg.providerApiKey = key;
79
+ saveConfig(cfg);
80
+ console.log(chalk.green('\n API key updated.\n'));
81
+ } else {
82
+ console.log(chalk.gray('\n No changes made.\n'));
83
+ }
84
+ }
85
+
86
+ function cmdChannels() {
87
+ console.log(chalk.cyan('\n Channels Setup\n'));
88
+ console.log(chalk.gray(' To connect a channel:'));
89
+ const channels = ['telegram', 'whatsapp', 'discord', 'slack', 'signal', 'irc', 'mattermost', 'imessage'];
90
+ for (const ch of channels) {
91
+ console.log(chalk.gray(' natureco ' + ch + ' connect'));
92
+ }
93
+ console.log('');
94
+ }
95
+
96
+ function cmdPlugins() {
97
+ console.log(chalk.cyan('\n Plugin Setup\n'));
98
+ console.log(chalk.gray(' To manage plugins:'));
99
+ console.log(chalk.cyan(' natureco plugins list'));
100
+ console.log(chalk.cyan(' natureco plugins add <url>\n'));
101
+ }
102
+
103
+ function cmdSkills() {
104
+ console.log(chalk.cyan('\n Skills Setup\n'));
105
+ console.log(chalk.gray(' To manage skills:'));
106
+ console.log(chalk.cyan(' natureco skills list'));
107
+ console.log(chalk.cyan(' natureco skills add <name>\n'));
108
+ }
109
+
110
+ module.exports = configure;
@@ -0,0 +1,92 @@
1
+ const chalk = require('chalk');
2
+ const { execSync } = require('child_process');
3
+
4
+ async function crestodian(args) {
5
+ const fixMode = args.includes('--fix') || args.includes('-f');
6
+
7
+ console.log(chalk.cyan('\n 🛠️ Crestodian — Setup & Repair Assistant\n'));
8
+ console.log(chalk.gray(' Diagnosing your NatureCo installation...\n'));
9
+
10
+ const checks = [];
11
+
12
+ // Check Node version
13
+ const nodeMajor = parseInt(process.version.slice(1).split('.')[0]);
14
+ checks.push({
15
+ name: 'Node.js version',
16
+ ok: nodeMajor >= 18,
17
+ message: `Node ${process.version} (18+ required)`,
18
+ fix: 'Install Node.js 18+: https://nodejs.org'
19
+ });
20
+
21
+ // Check npm
22
+ try {
23
+ const npmVer = execSync('npm --version', { encoding: 'utf8' }).trim();
24
+ checks.push({ name: 'npm', ok: true, message: `npm ${npmVer}` });
25
+ } catch {
26
+ checks.push({ name: 'npm', ok: false, message: 'npm not found', fix: 'Install Node.js (includes npm)' });
27
+ }
28
+
29
+ // Check config
30
+ try {
31
+ const { getConfig } = require('../utils/config');
32
+ const config = getConfig();
33
+ checks.push({
34
+ name: 'Configuration',
35
+ ok: !!(config.openaiApiKey || config.anthropicApiKey || config.provider),
36
+ message: config.provider ? `Provider: ${config.provider}` : 'Not configured',
37
+ fix: 'Run: natureco configure'
38
+ });
39
+ } catch {
40
+ checks.push({ name: 'Configuration', ok: false, message: 'Config error', fix: 'Run: natureco setup' });
41
+ }
42
+
43
+ // Check git
44
+ try {
45
+ execSync('git --version', { stdio: 'pipe', encoding: 'utf8' });
46
+ checks.push({ name: 'Git', ok: true, message: 'Available' });
47
+ } catch {
48
+ checks.push({ name: 'Git', ok: false, message: 'Not found', fix: 'Install git: https://git-scm.com' });
49
+ }
50
+
51
+ // Check Playwright
52
+ try {
53
+ require.resolve('playwright');
54
+ checks.push({ name: 'Playwright', ok: true, message: 'Installed' });
55
+ } catch {
56
+ checks.push({ name: 'Playwright', ok: true, message: 'Optional (not installed)', optional: true });
57
+ }
58
+
59
+ // Print results
60
+ const failed = checks.filter(c => !c.ok);
61
+ for (const check of checks) {
62
+ const icon = check.ok ? chalk.green('✅') : check.optional ? chalk.gray('⬜') : chalk.red('❌');
63
+ console.log(` ${icon} ${chalk.white(check.name)} ${chalk.gray('- ' + check.message)}`);
64
+ }
65
+
66
+ console.log(chalk.gray('\n ' + '─'.repeat(48)));
67
+
68
+ if (failed.length === 0) {
69
+ console.log(chalk.green('\n ✅ All checks passed!\n'));
70
+ } else {
71
+ console.log(chalk.yellow(`\n ⚠️ ${failed.length} issue(s) found:\n`));
72
+ for (const f of failed) {
73
+ console.log(` ${chalk.yellow('●')} ${f.name}: ${f.message}`);
74
+ if (fixMode && f.fix) {
75
+ console.log(` ${chalk.gray('Fix:')} ${f.fix}`);
76
+ }
77
+ }
78
+
79
+ if (fixMode) {
80
+ console.log(chalk.gray('\n Running fixes...\n'));
81
+ for (const f of failed) {
82
+ if (f.name === 'Configuration') {
83
+ console.log(chalk.gray(' Run: natureco onboard'));
84
+ }
85
+ }
86
+ }
87
+
88
+ console.log(chalk.gray('\n To auto-fix: ') + chalk.cyan('natureco crestodian --fix\n'));
89
+ }
90
+ }
91
+
92
+ module.exports = crestodian;