natureco-cli 2.23.30 → 2.23.32

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 (69) hide show
  1. package/bin/natureco.js +178 -167
  2. package/package.json +1 -1
  3. package/src/commands/acp.js +39 -0
  4. package/src/commands/admin-rpc.js +83 -0
  5. package/src/commands/agent.js +214 -23
  6. package/src/commands/agents.js +114 -30
  7. package/src/commands/approvals.js +172 -11
  8. package/src/commands/ask.js +1 -1
  9. package/src/commands/browser.js +815 -0
  10. package/src/commands/capability.js +195 -22
  11. package/src/commands/channels.js +422 -267
  12. package/src/commands/chat.js +5 -8
  13. package/src/commands/clawbot.js +19 -0
  14. package/src/commands/code.js +3 -2
  15. package/src/commands/commitments.js +125 -9
  16. package/src/commands/completion.js +40 -32
  17. package/src/commands/config.js +228 -30
  18. package/src/commands/configure.js +84 -67
  19. package/src/commands/cron.js +239 -19
  20. package/src/commands/daemon.js +34 -4
  21. package/src/commands/dashboard.js +47 -374
  22. package/src/commands/devices.js +53 -26
  23. package/src/commands/directory.js +146 -14
  24. package/src/commands/dns.js +148 -10
  25. package/src/commands/docs.js +119 -26
  26. package/src/commands/doctor.js +143 -492
  27. package/src/commands/exec-policy.js +57 -48
  28. package/src/commands/gateway.js +492 -249
  29. package/src/commands/health.js +141 -11
  30. package/src/commands/help.js +24 -25
  31. package/src/commands/hooks.js +141 -87
  32. package/src/commands/infer.js +1442 -41
  33. package/src/commands/logs.js +122 -99
  34. package/src/commands/mcp.js +121 -309
  35. package/src/commands/memory.js +128 -0
  36. package/src/commands/message.js +720 -140
  37. package/src/commands/models.js +39 -1
  38. package/src/commands/node.js +77 -77
  39. package/src/commands/nodes.js +278 -22
  40. package/src/commands/onboard.js +115 -56
  41. package/src/commands/pairing.js +108 -107
  42. package/src/commands/path.js +206 -0
  43. package/src/commands/plugins.js +35 -1
  44. package/src/commands/proxy.js +159 -8
  45. package/src/commands/qr.js +55 -13
  46. package/src/commands/reset.js +101 -94
  47. package/src/commands/secrets.js +104 -21
  48. package/src/commands/sessions.js +110 -51
  49. package/src/commands/setup.js +229 -649
  50. package/src/commands/skills.js +67 -1
  51. package/src/commands/status.js +101 -127
  52. package/src/commands/tasks.js +208 -100
  53. package/src/commands/terminal.js +130 -12
  54. package/src/commands/transcripts.js +24 -1
  55. package/src/commands/tui.js +41 -0
  56. package/src/commands/uninstall.js +73 -92
  57. package/src/commands/update.js +146 -91
  58. package/src/commands/web-fetch.js +34 -0
  59. package/src/commands/webhooks.js +58 -66
  60. package/src/commands/wiki.js +783 -0
  61. package/src/utils/agents-md.js +85 -0
  62. package/src/utils/api.js +40 -41
  63. package/src/utils/format.js +144 -0
  64. package/src/utils/headless.js +2 -1
  65. package/src/utils/parallel-tools.js +106 -0
  66. package/src/utils/sub-agent.js +148 -0
  67. package/src/utils/token-budget.js +304 -0
  68. package/src/utils/tool-runner.js +7 -5
  69. package/src/utils/web-fetch.js +107 -0
@@ -1,18 +1,148 @@
1
1
  const chalk = require('chalk');
2
+ const F = require('../utils/format');
3
+ const fs = require('fs');
4
+ const path = require('path');
2
5
  const os = require('os');
3
6
 
7
+ const CHECKS = {
8
+ config: { label: 'Configuration', desc: 'Check if config file exists and is valid' },
9
+ gateway: { label: 'Gateway', desc: 'Check if gateway is running' },
10
+ nodes: { label: 'Nodes', desc: 'Check node connectivity' },
11
+ disk: { label: 'Disk', desc: 'Check disk space for ~/.natureco' },
12
+ };
13
+
4
14
  function health(args) {
5
- console.log(chalk.cyan('\n šŸ„ System Health\n'));
6
- console.log(chalk.gray(' ' + '─'.repeat(48)));
7
- console.log(` ${chalk.white('Status:')} ${chalk.green('Healthy')}`);
8
- console.log(` ${chalk.white('Node:')} ${process.version}`);
9
- console.log(` ${chalk.white('Platform:')} ${process.platform} ${os.release()}`);
10
- console.log(` ${chalk.white('Uptime:')} ${Math.floor(process.uptime())}s`);
11
- console.log(` ${chalk.white('Memory:')} ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB / ${Math.round(process.memoryUsage().heapTotal / 1024 / 1024)}MB`);
12
- console.log(` ${chalk.white('CPU:')} ${os.cpus()[0]?.model || 'unknown'}`);
13
- console.log(` ${chalk.white('Load:')} ${os.loadavg()[0].toFixed(2)}`);
14
- console.log(` ${chalk.white('Host:')} ${os.hostname()}`);
15
- console.log();
15
+ const [action, ...params] = args || [];
16
+
17
+ if (!action || action === 'run') return cmdRun();
18
+ if (action === 'list') return cmdList();
19
+ if (action === 'check') return cmdCheck(params[0]);
20
+
21
+ console.log(chalk.red(`\n Unknown health action: ${action}\n`));
22
+ console.log(chalk.gray(' Usage: natureco health <action> [params]'));
23
+ console.log(chalk.gray(' Actions: run, list, check <name>\n'));
24
+ process.exit(1);
25
+ }
26
+
27
+ function cmdRun() {
28
+ const results = [];
29
+
30
+ results.push(runCheck('config', checkConfig));
31
+ results.push(runCheck('gateway', checkGateway));
32
+ results.push(runCheck('nodes', checkNodes));
33
+ results.push(runCheck('disk', checkDisk));
34
+
35
+ const passed = results.filter(r => r.status === 'pass').length;
36
+ const failed = results.filter(r => r.status === 'fail').length;
37
+ const warnings = results.filter(r => r.status === 'warn').length;
38
+
39
+ F.header('Health Check');
40
+
41
+ const statusMap = { pass: 'āœ“', fail: 'āœ—', warn: '⚠' };
42
+ const rows = results.map(r => [
43
+ r.label,
44
+ statusMap[r.status] || '?',
45
+ r.message,
46
+ ]);
47
+ F.table(['Check', 'Status', 'Detail'], rows);
48
+
49
+ for (const r of results) {
50
+ F.dot(r.status === 'pass', r.label);
51
+ }
52
+
53
+ const summary = `${passed} passed, ${warnings} warnings, ${failed} failed`;
54
+ if (failed > 0) {
55
+ F.error(summary);
56
+ } else {
57
+ F.success(summary);
58
+ }
59
+ }
60
+
61
+ function cmdList() {
62
+ F.list(Object.entries(CHECKS).map(([key, check]) => ({
63
+ label: key,
64
+ desc: `${check.label} — ${check.desc}`,
65
+ })));
66
+ F.info(`Run all: ${F.cmd('natureco health run')}`);
67
+ F.info(`Run one: ${F.cmd('natureco health check <name>')}`);
68
+ }
69
+
70
+ function cmdCheck(name) {
71
+ if (!name || !CHECKS[name]) {
72
+ console.log(chalk.red(`\n Unknown check: ${name}\n`));
73
+ console.log(chalk.gray(' Available checks: ' + Object.keys(CHECKS).join(', ') + '\n'));
74
+ process.exit(1);
75
+ }
76
+
77
+ const handlers = { config: checkConfig, gateway: checkGateway, nodes: checkNodes, disk: checkDisk };
78
+ const result = runCheck(name, handlers[name]);
79
+
80
+ if (result.status === 'pass') {
81
+ F.success(`${result.label}: ${result.message}`);
82
+ } else if (result.status === 'warn') {
83
+ F.warning(`${result.label}: ${result.message}`);
84
+ } else {
85
+ F.error(`${result.label}: ${result.message}`);
86
+ }
87
+ }
88
+
89
+ function runCheck(name, fn) {
90
+ try { return { name, ...CHECKS[name], ...fn() }; }
91
+ catch (err) { return { name, ...CHECKS[name], status: 'fail', message: err.message }; }
92
+ }
93
+
94
+ function checkConfig() {
95
+ const configDir = path.join(os.homedir(), '.natureco');
96
+ const configFile = path.join(configDir, 'config.json');
97
+
98
+ if (!fs.existsSync(configDir)) return { status: 'fail', message: '~/.natureco directory does not exist' };
99
+ if (!fs.existsSync(configFile)) return { status: 'fail', message: 'config.json not found' };
100
+
101
+ try {
102
+ JSON.parse(fs.readFileSync(configFile, 'utf8'));
103
+ return { status: 'pass', message: 'Config file exists and is valid' };
104
+ } catch {
105
+ return { status: 'fail', message: 'config.json is not valid JSON' };
106
+ }
107
+ }
108
+
109
+ function checkGateway() {
110
+ const pidFile = path.join(os.homedir(), '.natureco', 'gateway.pid');
111
+ if (!fs.existsSync(pidFile)) return { status: 'warn', message: 'Gateway is not running' };
112
+
113
+ try {
114
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
115
+ process.kill(pid, 0);
116
+ return { status: 'pass', message: `Gateway running (PID: ${pid})` };
117
+ } catch {
118
+ return { status: 'warn', message: 'Gateway PID file exists but process is not running' };
119
+ }
120
+ }
121
+
122
+ function checkNodes() {
123
+ const nodesFile = path.join(os.homedir(), '.natureco', 'nodes.json');
124
+ if (!fs.existsSync(nodesFile)) return { status: 'warn', message: 'No nodes configured' };
125
+
126
+ try {
127
+ const nodes = JSON.parse(fs.readFileSync(nodesFile, 'utf8'));
128
+ const names = Object.keys(nodes);
129
+ const online = names.filter(n => nodes[n].online);
130
+ return { status: 'pass', message: `${online.length}/${names.length} nodes online` };
131
+ } catch {
132
+ return { status: 'fail', message: 'nodes.json is invalid' };
133
+ }
134
+ }
135
+
136
+ function checkDisk() {
137
+ const dir = path.join(os.homedir(), '.natureco');
138
+ if (!fs.existsSync(dir)) return { status: 'warn', message: '~/.natureco does not exist' };
139
+
140
+ try {
141
+ const stat = fs.statSync(dir);
142
+ return { status: 'pass', message: `Directory exists, last modified: ${stat.mtime.toISOString().slice(0, 10)}` };
143
+ } catch {
144
+ return { status: 'fail', message: 'Cannot access ~/.natureco' };
145
+ }
16
146
  }
17
147
 
18
148
  module.exports = health;
@@ -1,4 +1,5 @@
1
1
  const chalk = require('chalk');
2
+ const F = require('../utils/format');
2
3
  const { getConfig } = require('../utils/config');
3
4
 
4
5
  function help() {
@@ -15,10 +16,10 @@ function help() {
15
16
  console.log('');
16
17
  console.log(chalk.green.bold(` NatureCo CLI `) + chalk.gray(`v${version}`));
17
18
  console.log(chalk.gray(' Terminal-native AI agent\n'));
18
- console.log(chalk.gray(' ' + '─'.repeat(48)));
19
+ F.divider();
19
20
 
20
21
  // ── Kurulum & Giriş ──────────────────────────────────────────
21
- console.log(chalk.cyan.bold('\n Kurulum & Giriş\n'));
22
+ F.section('Kurulum & Giriş');
22
23
  printCmd('natureco setup', 'İlk kurulum sihirbazı (provider, bot, entegrasyon)');
23
24
  printCmd('natureco login', 'API key ile giriş yap');
24
25
  printCmd('natureco logout', 'Ƈıkış yap');
@@ -26,7 +27,7 @@ function help() {
26
27
  printCmd('natureco doctor', 'Sistem sağlığı kontrolü');
27
28
 
28
29
  // ── Chat ─────────────────────────────────────────────────────
29
- console.log(chalk.cyan.bold('\n Chat\n'));
30
+ F.section('Chat');
30
31
  printCmd('natureco chat', 'Varsayılan bot ile sohbet başlat');
31
32
  printCmd('natureco chat <bot>', 'Belirli bot ile sohbet (boşluklu isim desteklenir)');
32
33
  printCmd('natureco chat --resume', 'Son oturuma devam et');
@@ -35,7 +36,7 @@ function help() {
35
36
  printCmd('natureco bots', 'Bot listesini gƶster');
36
37
 
37
38
  // ── Entegrasyonlar ───────────────────────────────────────────
38
- console.log(chalk.cyan.bold('\n Entegrasyonlar\n'));
39
+ F.section('Entegrasyonlar');
39
40
  printCmd('natureco telegram connect', 'Telegram bot bağla');
40
41
  printCmd('natureco whatsapp connect', 'WhatsApp QR kod ile bağla');
41
42
  printCmd('natureco discord connect', 'Discord bot bağla');
@@ -48,14 +49,14 @@ function help() {
48
49
  printCmd('natureco webhooks connect', 'Webhook ekle');
49
50
 
50
51
  // ── Gateway & Dashboard ──────────────────────────────────────
51
- console.log(chalk.cyan.bold('\n Gateway & Dashboard\n'));
52
+ F.section('Gateway & Dashboard');
52
53
  printCmd('natureco gateway start', 'Gateway\'i arka planda başlat (WhatsApp dahil)');
53
54
  printCmd('natureco gateway stop', 'Gateway\'i durdur');
54
55
  printCmd('natureco gateway status', 'Gateway durumu ve son loglar');
55
56
  printCmd('natureco dashboard', 'Web arayüzünü aƧ (localhost:3848)');
56
57
 
57
58
  // ── Skill & MCP ──────────────────────────────────────────────
58
- console.log(chalk.cyan.bold('\n Skill & MCP\n'));
59
+ F.section('Skill & MCP');
59
60
  printCmd('natureco skills', 'Yüklü skill\'leri listele');
60
61
  printCmd('natureco skills install <s>', 'NatureHub\'dan skill yükle');
61
62
  printCmd('natureco skills install clawhub:<s>', 'ClawHub\'dan skill yükle');
@@ -67,7 +68,7 @@ function help() {
67
68
  printCmd('natureco mcp templates', 'Hazır MCP şablonları');
68
69
 
69
70
  // ── Otomasyon ────────────────────────────────────────────────
70
- console.log(chalk.cyan.bold('\n Otomasyon\n'));
71
+ F.section('Otomasyon');
71
72
  printCmd('natureco cron add', 'Zamanlanmış gƶrev oluştur');
72
73
  printCmd('natureco cron list', 'Cron gƶrevlerini listele');
73
74
  printCmd('natureco cron remove', 'Cron gƶrevi sil');
@@ -79,7 +80,7 @@ function help() {
79
80
  printCmd('natureco migrate --from openclaw', 'OpenClaw\'dan geƧiş');
80
81
 
81
82
  // ── Diğer ────────────────────────────────────────────────────
82
- console.log(chalk.cyan.bold('\n Diğer\n'));
83
+ F.section('Diğer');
83
84
  printCmd('natureco channels [action]', 'Kanal yƶnetimi (list|status|remove|logs)');
84
85
  printCmd('natureco models [action]', 'Model yƶnetimi (list|set|scan|aliases)');
85
86
  printCmd('natureco memory [action]', 'Hafıza yƶnetimi (status|list|search|show|clear)');
@@ -98,8 +99,8 @@ function help() {
98
99
  printCmd('natureco init', 'Proje klasƶrü oluştur (.natureco/)');
99
100
 
100
101
  // ── Chat iƧi komutlar ────────────────────────────────────────
101
- console.log(chalk.cyan.bold('\n Chat İƧi Komutlar\n'));
102
- console.log(chalk.gray(' ' + '─'.repeat(48)));
102
+ F.section('Chat İƧi Komutlar');
103
+ F.divider();
103
104
  printInChat('/clear', 'Ekranı temizle');
104
105
  printInChat('/bot [ad]', 'Bot değiştir');
105
106
  printInChat('/skills', 'Aktif skill\'leri gƶster');
@@ -113,42 +114,40 @@ function help() {
113
114
 
114
115
  // ── Mevcut config ƶzeti ──────────────────────────────────────
115
116
  if (config.providerUrl || config.botName) {
116
- console.log(chalk.cyan.bold('\n Mevcut Yapılandırma\n'));
117
+ F.section('Mevcut Yapılandırma');
117
118
  if (config.providerUrl) {
118
119
  const provider = config.providerUrl.replace('https://', '').split('/')[0];
119
- console.log(chalk.gray(' Provider : ') + chalk.white(provider));
120
+ F.kv('Provider', provider);
120
121
  }
121
122
  if (config.providerModel) {
122
- console.log(chalk.gray(' Model : ') + chalk.white(config.providerModel));
123
+ F.kv('Model', config.providerModel);
123
124
  }
124
125
  if (config.botName) {
125
- console.log(chalk.gray(' Bot : ') + chalk.cyan(config.botName));
126
+ F.kv('Bot', config.botName);
126
127
  }
127
128
  if (config.userName) {
128
- console.log(chalk.gray(' Kullanıcı: ') + chalk.white(config.userName));
129
+ F.kv('Kullanıcı', config.userName);
129
130
  }
130
131
  }
131
132
 
132
- // ── Footer ───────────────────────────────────────────────────
133
- console.log('');
134
- console.log(chalk.gray(' ' + '─'.repeat(48)));
135
- console.log(chalk.gray(' Döküman : ') + chalk.cyan('natureco.me/cli'));
136
- console.log(chalk.gray(' API Key : ') + chalk.cyan('developers.natureco.me'));
137
- console.log(chalk.gray(' npm : ') + chalk.cyan('npmjs.com/package/natureco-cli'));
133
+ F.divider();
134
+ F.kv('Döküman', 'natureco.me/cli');
135
+ F.kv('API Key', 'developers.natureco.me');
136
+ F.kv('npm', 'npmjs.com/package/natureco-cli');
138
137
  console.log('');
139
138
  }
140
139
 
141
140
  function printCmd(cmd, desc) {
142
141
  console.log(
143
- chalk.yellow(' ' + cmd.padEnd(38)) +
144
- chalk.gray(desc)
142
+ ' ' + F.flag(cmd.padEnd(38)) +
143
+ chalk.dim(desc)
145
144
  );
146
145
  }
147
146
 
148
147
  function printInChat(cmd, desc) {
149
148
  console.log(
150
- chalk.green(' ' + cmd.padEnd(20)) +
151
- chalk.gray(desc)
149
+ ' ' + F.cmd(cmd.padEnd(20)) +
150
+ chalk.dim(desc)
152
151
  );
153
152
  }
154
153
 
@@ -1,87 +1,141 @@
1
- const inquirer = require('../utils/inquirer-wrapper');
2
- const chalk = require('chalk');
3
- const { getAllHooks, createHook } = require('../utils/hooks');
4
-
5
- async function hooks(action, ...args) {
6
- if (!action || action === 'list') {
7
- return listHooks();
8
- }
9
-
10
- if (action === 'create') {
11
- const type = args[0];
12
- if (!type) {
13
- console.log(chalk.red('\nāŒ Hook type required\n'));
14
- console.log(chalk.gray('Available types: pre-message, post-message, on-start, on-exit\n'));
15
- console.log(chalk.gray('Usage: natureco hooks create <type>\n'));
16
- process.exit(1);
17
- }
18
- return createHookInteractive(type);
19
- }
20
-
21
- console.log(chalk.red(`\nāŒ Unknown action: ${action}\n`));
22
- console.log(chalk.gray('Available actions: list, create\n'));
23
- process.exit(1);
24
- }
25
-
26
- function listHooks() {
27
- const hooks = getAllHooks();
28
-
29
- if (hooks.length === 0) {
30
- console.log(chalk.gray('\nNo hooks found.\n'));
31
- console.log(chalk.gray('Create one with: natureco hooks create <type>\n'));
32
- return;
33
- }
34
-
35
- console.log(chalk.yellow('\nHooks:\n'));
36
-
37
- const grouped = {};
38
- hooks.forEach(hook => {
39
- if (!grouped[hook.type]) grouped[hook.type] = [];
40
- grouped[hook.type].push(hook);
41
- });
42
-
43
- Object.keys(grouped).forEach(type => {
44
- console.log(chalk.cyan(` ${type}:`));
45
- grouped[type].forEach(hook => {
46
- const sourceLabel = hook.source === 'user' ? chalk.blue('[global]') : chalk.green('[project]');
47
- console.log(` ${sourceLabel} ${chalk.white(hook.name)}`);
48
- });
49
- console.log('');
50
- });
51
- }
52
-
53
- async function createHookInteractive(type) {
54
- const validTypes = ['pre-message', 'post-message', 'on-start', 'on-exit'];
55
-
56
- if (!validTypes.includes(type)) {
57
- console.log(chalk.red(`\nāŒ Invalid hook type: ${type}\n`));
58
- console.log(chalk.gray('Available types: pre-message, post-message, on-start, on-exit\n'));
59
- process.exit(1);
60
- }
61
-
62
- process.stdin.resume();
63
-
64
- const answers = await inquirer.prompt([
65
- {
66
- type: 'list',
67
- name: 'scope',
68
- message: 'Hook scope:',
69
- choices: [
70
- { name: 'Project (only this project)', value: 'project' },
71
- { name: 'Global (all projects)', value: 'user' },
72
- ],
73
- default: 'project',
74
- },
75
- ]);
76
-
77
- try {
78
- const filePath = createHook(type, answers.scope);
79
- console.log(chalk.green(`\nāœ… Hook created: ${filePath}\n`));
80
- console.log(chalk.gray('Edit the file to customize the hook behavior.\n'));
81
- } catch (err) {
82
- console.log(chalk.red(`\nāŒ Error: ${err.message}\n`));
83
- process.exit(1);
84
- }
85
- }
86
-
87
- module.exports = hooks;
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ const HOOKS_FILE = path.join(os.homedir(), '.natureco', 'hooks.json');
7
+
8
+ function loadHooks() {
9
+ if (!fs.existsSync(HOOKS_FILE)) return [];
10
+ try { return JSON.parse(fs.readFileSync(HOOKS_FILE, 'utf8')); }
11
+ catch { return []; }
12
+ }
13
+
14
+ function saveHooks(hooks) {
15
+ const dir = path.dirname(HOOKS_FILE);
16
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
17
+ fs.writeFileSync(HOOKS_FILE, JSON.stringify(hooks, null, 2), 'utf8');
18
+ }
19
+
20
+ function hooks(args) {
21
+ const [action, ...params] = args || [];
22
+
23
+ if (!action || action === 'list') return cmdList();
24
+ if (action === 'info') return cmdInfo(params[0]);
25
+ if (action === 'check') return cmdCheck(params[0]);
26
+ if (action === 'enable') return cmdEnable(params[0]);
27
+ if (action === 'disable') return cmdDisable(params[0]);
28
+ if (action === 'install') return cmdInstall(params[0]);
29
+ if (action === 'update') return cmdUpdate(params[0]);
30
+
31
+ console.log(chalk.red(`\n Unknown hooks action: ${action}\n`));
32
+ console.log(chalk.gray(' Usage: natureco hooks <action> [params]'));
33
+ console.log(chalk.gray(' Actions: list, info <name>, check <name>, enable <name>, disable <name>, install <url>, update <name>\n'));
34
+ process.exit(1);
35
+ }
36
+
37
+ function cmdList() {
38
+ const hooks = loadHooks();
39
+
40
+ console.log(chalk.cyan(`\n Hooks (${hooks.length})\n`));
41
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
42
+
43
+ if (hooks.length === 0) {
44
+ console.log(chalk.gray(' No hooks installed.\n'));
45
+ return;
46
+ }
47
+
48
+ for (const h of hooks) {
49
+ const status = h.disabled ? chalk.red('disabled') : chalk.green('enabled');
50
+ console.log(` ${chalk.white(h.name)} ${chalk.gray(`— ${status}`)}`);
51
+ if (h.type) console.log(chalk.gray(` Type: ${h.type}`));
52
+ if (h.url) console.log(chalk.gray(` URL: ${h.url}`));
53
+ }
54
+ console.log('');
55
+ }
56
+
57
+ function cmdInfo(name) {
58
+ if (!name) { console.log(chalk.red('\n Usage: natureco hooks info <name>\n')); process.exit(1); }
59
+
60
+ const hooks = loadHooks();
61
+ const h = hooks.find(x => x.name === name);
62
+
63
+ if (!h) { console.log(chalk.yellow(`\n Hook "${name}" not found.\n`)); return; }
64
+
65
+ console.log(chalk.cyan(`\n Hook: ${h.name}\n`));
66
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
67
+ for (const [key, val] of Object.entries(h)) {
68
+ console.log(` ${chalk.white(key)}: ${chalk.white(typeof val === 'object' ? JSON.stringify(val) : val)}`);
69
+ }
70
+ console.log('');
71
+ }
72
+
73
+ function cmdCheck(name) {
74
+ if (!name) { console.log(chalk.red('\n Usage: natureco hooks check <name>\n')); process.exit(1); }
75
+
76
+ const hooks = loadHooks();
77
+ const h = hooks.find(x => x.name === name);
78
+
79
+ if (!h) { console.log(chalk.yellow(`\n Hook "${name}" not found.\n`)); return; }
80
+
81
+ const issues = [];
82
+ if (!h.type) issues.push('Missing type');
83
+ if (!h.run) issues.push('Missing run script/command');
84
+ if (h.disabled) issues.push('Hook is disabled');
85
+
86
+ if (issues.length === 0) {
87
+ console.log(chalk.green(`\n Hook "${name}" is valid.\n`));
88
+ } else {
89
+ console.log(chalk.yellow(`\n Hook "${name}" has ${issues.length} issue(s):\n`));
90
+ for (const issue of issues) console.log(chalk.yellow(` - ${issue}`));
91
+ console.log('');
92
+ }
93
+ }
94
+
95
+ function cmdEnable(name) {
96
+ if (!name) { console.log(chalk.red('\n Usage: natureco hooks enable <name>\n')); process.exit(1); }
97
+
98
+ const hooks = loadHooks();
99
+ const h = hooks.find(x => x.name === name);
100
+ if (!h) { console.log(chalk.yellow(`\n Hook "${name}" not found.\n`)); return; }
101
+
102
+ h.disabled = false;
103
+ saveHooks(hooks);
104
+ console.log(chalk.green(`\n Hook "${name}" enabled.\n`));
105
+ }
106
+
107
+ function cmdDisable(name) {
108
+ if (!name) { console.log(chalk.red('\n Usage: natureco hooks disable <name>\n')); process.exit(1); }
109
+
110
+ const hooks = loadHooks();
111
+ const h = hooks.find(x => x.name === name);
112
+ if (!h) { console.log(chalk.yellow(`\n Hook "${name}" not found.\n`)); return; }
113
+
114
+ h.disabled = true;
115
+ saveHooks(hooks);
116
+ console.log(chalk.gray(`\n Hook "${name}" disabled.\n`));
117
+ }
118
+
119
+ function cmdInstall(url) {
120
+ if (!url) { console.log(chalk.red('\n Usage: natureco hooks install <url>\n')); process.exit(1); }
121
+
122
+ const hooks = loadHooks();
123
+ const name = path.basename(url, path.extname(url));
124
+ hooks.push({ name, url, type: 'custom', disabled: false, installedAt: new Date().toISOString() });
125
+ saveHooks(hooks);
126
+ console.log(chalk.green(`\n Hook "${name}" installed from ${url}.\n`));
127
+ }
128
+
129
+ function cmdUpdate(name) {
130
+ if (!name) { console.log(chalk.red('\n Usage: natureco hooks update <name>\n')); process.exit(1); }
131
+
132
+ const hooks = loadHooks();
133
+ const h = hooks.find(x => x.name === name);
134
+ if (!h) { console.log(chalk.yellow(`\n Hook "${name}" not found.\n`)); return; }
135
+
136
+ h.updatedAt = new Date().toISOString();
137
+ saveHooks(hooks);
138
+ console.log(chalk.green(`\n Hook "${name}" updated.\n`));
139
+ }
140
+
141
+ module.exports = hooks;