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
@@ -0,0 +1,148 @@
1
+ const chalk = require('chalk');
2
+ const F = require('../utils/format');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
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
+
14
+ function health(args) {
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
+ }
146
+ }
147
+
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(`\nHook 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;