chainlesschain 0.38.1 → 0.40.2

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.
package/README.md CHANGED
@@ -169,7 +169,7 @@ chainlesschain llm models # List installed Ollama models
169
169
  chainlesschain llm models --json # JSON output
170
170
  chainlesschain llm test # Test Ollama connectivity
171
171
  chainlesschain llm test --provider openai --api-key sk-...
172
- chainlesschain llm providers # List 7 built-in LLM providers
172
+ chainlesschain llm providers # List 8 built-in LLM providers
173
173
  chainlesschain llm add-provider <name> # Add custom provider
174
174
  chainlesschain llm switch <name> # Switch active provider
175
175
  ```
@@ -638,6 +638,198 @@ chainlesschain lowcode publish <id> # Publish app
638
638
 
639
639
  ---
640
640
 
641
+ ## EvoMap Gene Exchange Protocol
642
+
643
+ ### `chainlesschain evomap <action>`
644
+
645
+ Gene exchange protocol for sharing AI capabilities across instances.
646
+
647
+ ```bash
648
+ chainlesschain evomap search "tool name" # Search genes on hub
649
+ chainlesschain evomap download <gene-id> # Download gene
650
+ chainlesschain evomap publish --name "my-gene" # Publish gene to hub
651
+ chainlesschain evomap list # List local genes
652
+ chainlesschain evomap hubs # List available hubs
653
+ chainlesschain evomap federation list-hubs # List federated hubs
654
+ chainlesschain evomap federation sync <hub> # Sync genes with hub
655
+ chainlesschain evomap federation pressure # Pressure analytics report
656
+ chainlesschain evomap gov propose "title" # Governance proposal
657
+ chainlesschain evomap gov vote <id> for # Vote on proposal
658
+ chainlesschain evomap gov dashboard # Governance dashboard
659
+ ```
660
+
661
+ ---
662
+
663
+ ## DAO Governance
664
+
665
+ ### `chainlesschain dao <action>`
666
+
667
+ Decentralized governance with quadratic voting.
668
+
669
+ ```bash
670
+ chainlesschain dao propose "title" # Create DAO proposal
671
+ chainlesschain dao vote <id> for # Vote (quadratic voting)
672
+ chainlesschain dao delegate <from> <to> # Delegate voting power
673
+ chainlesschain dao execute <id> # Execute passed proposal
674
+ chainlesschain dao treasury # Show treasury balance
675
+ chainlesschain dao stats # Governance statistics
676
+ ```
677
+
678
+ ---
679
+
680
+ ## Phase 8: Security & Compliance
681
+
682
+ ### `chainlesschain compliance <action>`
683
+
684
+ Compliance evidence collection and reporting (GDPR, SOC2, HIPAA).
685
+
686
+ ```bash
687
+ chainlesschain compliance evidence gdpr # Collect compliance evidence
688
+ chainlesschain compliance report soc2 # Generate compliance report
689
+ chainlesschain compliance classify "text" # Classify data sensitivity
690
+ chainlesschain compliance scan hipaa # Scan compliance posture
691
+ ```
692
+
693
+ ### `chainlesschain dlp <action>`
694
+
695
+ Data Loss Prevention (DLP) content scanning and policy management.
696
+
697
+ ```bash
698
+ chainlesschain dlp scan "content" # DLP content scanning
699
+ chainlesschain dlp incidents # List DLP incidents
700
+ chainlesschain dlp policy create --name "rule" # Create DLP policy
701
+ ```
702
+
703
+ ### `chainlesschain siem <action>`
704
+
705
+ Security Information and Event Management (SIEM) integration.
706
+
707
+ ```bash
708
+ chainlesschain siem targets # List SIEM targets
709
+ chainlesschain siem add-target splunk_hec <url> # Add SIEM export target
710
+ chainlesschain siem export <target-id> # Export logs to SIEM
711
+ ```
712
+
713
+ ### `chainlesschain pqc <action>`
714
+
715
+ Post-Quantum Cryptography key management and migration.
716
+
717
+ ```bash
718
+ chainlesschain pqc keys # List PQC keys
719
+ chainlesschain pqc generate ML-KEM-768 # Generate PQC key pair
720
+ chainlesschain pqc migration-status # PQC migration status
721
+ chainlesschain pqc migrate "plan" ML-KEM-768 # Execute PQC migration
722
+ ```
723
+
724
+ ---
725
+
726
+ ## Phase 8: Communication Bridges
727
+
728
+ ### `chainlesschain nostr <action>`
729
+
730
+ Nostr protocol bridge for decentralized social messaging.
731
+
732
+ ```bash
733
+ chainlesschain nostr relays # List Nostr relays
734
+ chainlesschain nostr publish "Hello" # Publish Nostr event
735
+ chainlesschain nostr keygen # Generate Nostr keypair
736
+ chainlesschain nostr map-did <did> <pubkey> # Map DID to Nostr
737
+ ```
738
+
739
+ ### `chainlesschain matrix <action>`
740
+
741
+ Matrix protocol bridge for federated messaging.
742
+
743
+ ```bash
744
+ chainlesschain matrix login # Login to Matrix
745
+ chainlesschain matrix rooms # List Matrix rooms
746
+ chainlesschain matrix send <room> "message" # Send Matrix message
747
+ ```
748
+
749
+ ### `chainlesschain scim <action>`
750
+
751
+ SCIM protocol for enterprise user provisioning.
752
+
753
+ ```bash
754
+ chainlesschain scim users list # List SCIM users
755
+ chainlesschain scim users create --name "user" # Create SCIM user
756
+ chainlesschain scim sync <connector-id> # Trigger SCIM sync
757
+ ```
758
+
759
+ ---
760
+
761
+ ## Phase 8: Infrastructure & Hardening
762
+
763
+ ### `chainlesschain terraform <action>`
764
+
765
+ Infrastructure-as-Code workspace management.
766
+
767
+ ```bash
768
+ chainlesschain terraform workspaces # List Terraform workspaces
769
+ chainlesschain terraform create "prod" # Create workspace
770
+ chainlesschain terraform plan <workspace-id> # Run Terraform plan
771
+ ```
772
+
773
+ ### `chainlesschain hardening <action>`
774
+
775
+ Security hardening and performance baseline management.
776
+
777
+ ```bash
778
+ chainlesschain hardening baseline collect "v1" # Collect performance baseline
779
+ chainlesschain hardening baseline compare <id> # Compare baseline (regression)
780
+ chainlesschain hardening audit run "quarterly" # Run security audit
781
+ ```
782
+
783
+ ---
784
+
785
+ ## Phase 8: Social Platform
786
+
787
+ ### `chainlesschain social <action>`
788
+
789
+ Decentralized social networking features.
790
+
791
+ ```bash
792
+ chainlesschain social contact add "Alice" # Add a contact
793
+ chainlesschain social contact list # List contacts
794
+ chainlesschain social friend add <contact-id> # Send friend request
795
+ chainlesschain social post publish "Hello" # Publish a post
796
+ chainlesschain social chat send <user> "msg" # Send chat message
797
+ chainlesschain social stats # Social statistics
798
+ ```
799
+
800
+ ---
801
+
802
+ ## CLI-Anything: Agent-Native Software Integration
803
+
804
+ ### `chainlesschain cli-anything <action>`
805
+
806
+ Discover and register external CLI tools as ChainlessChain skills.
807
+
808
+ ```bash
809
+ chainlesschain cli-anything doctor # Check Python + CLI-Anything environment
810
+ chainlesschain cli-anything scan # Scan PATH for cli-anything-* tools
811
+ chainlesschain cli-anything register <name> # Register tool as ChainlessChain skill
812
+ chainlesschain cli-anything list # List registered tools
813
+ chainlesschain cli-anything remove <name> # Remove registered tool
814
+ ```
815
+
816
+ ---
817
+
818
+ ## WebSocket Server Interface
819
+
820
+ ### `chainlesschain serve`
821
+
822
+ Start a WebSocket server for external tool integration, enabling real-time bidirectional communication with the CLI engine.
823
+
824
+ ```bash
825
+ chainlesschain serve # Start WebSocket server (port 18800)
826
+ chainlesschain serve --port 9000 # Custom port
827
+ chainlesschain serve --token <secret> # Enable token auth
828
+ chainlesschain serve --allow-remote --token <secret> # Allow remote + auth
829
+ ```
830
+
831
+ ---
832
+
641
833
  ## Global Options
642
834
 
643
835
  ```bash
@@ -682,16 +874,17 @@ Configuration is stored at `~/.chainlesschain/config.json`. The CLI creates and
682
874
 
683
875
  ### Supported LLM Providers
684
876
 
685
- | Provider | Default Model | API Key Required |
686
- | ------------------- | ----------------- | ---------------- |
687
- | Ollama (Local) | qwen2:7b | No |
688
- | OpenAI | gpt-4o | Yes |
689
- | Anthropic | claude-sonnet-4-6 | Yes |
690
- | DashScope (Alibaba) | qwen-max | Yes |
691
- | DeepSeek | deepseek-chat | Yes |
692
- | Gemini (Google) | gemini-pro | Yes |
693
- | Mistral | mistral-large | Yes |
694
- | Custom | | Yes |
877
+ | Provider | Default Model | API Key Required |
878
+ | ---------------------- | ----------------- | ---------------- |
879
+ | Ollama (Local) | qwen2:7b | No |
880
+ | OpenAI | gpt-4o | Yes |
881
+ | Anthropic | claude-sonnet-4-6 | Yes |
882
+ | DashScope (Alibaba) | qwen-max | Yes |
883
+ | DeepSeek | deepseek-chat | Yes |
884
+ | Gemini (Google) | gemini-pro | Yes |
885
+ | Mistral | mistral-large | Yes |
886
+ | Volcengine (ByteDance) | doubao-1.5-pro | Yes |
887
+ | Custom | — | Yes |
695
888
 
696
889
  ## File Structure
697
890
 
@@ -710,7 +903,7 @@ Configuration is stored at `~/.chainlesschain/config.json`. The CLI creates and
710
903
  ```bash
711
904
  cd packages/cli
712
905
  npm install
713
- npm test # Run all tests (1429 tests across 67 files)
906
+ npm test # Run all tests (2063 tests across 99 files)
714
907
  npm run test:unit # Unit tests only
715
908
  npm run test:integration # Integration tests
716
909
  npm run test:e2e # End-to-end tests
@@ -720,13 +913,13 @@ npm run test:e2e # End-to-end tests
720
913
 
721
914
  | Category | Files | Tests | Status |
722
915
  | ------------------------ | ------ | -------- | --------------- |
723
- | Unit — lib modules | 44 | 892 | All passing |
724
- | Unit — commands | 10 | 213 | All passing |
916
+ | Unit — lib modules | 56 | 1200+ | All passing |
917
+ | Unit — commands | 15 | 350+ | All passing |
725
918
  | Unit — runtime | 1 | 6 | All passing |
726
- | Integration | 3 | 7 | All passing |
727
- | E2E | 10 | 109 | All passing |
919
+ | Integration | 5 | 30+ | All passing |
920
+ | E2E | 14 | 150+ | All passing |
728
921
  | Core packages (external) | — | 118 | All passing |
729
- | **CLI Total** | **67** | **1429** | **All passing** |
922
+ | **CLI Total** | **99** | **2063** | **All passing** |
730
923
 
731
924
  ## License
732
925
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chainlesschain",
3
- "version": "0.38.1",
3
+ "version": "0.40.2",
4
4
  "description": "CLI for ChainlessChain - install, configure, and manage your personal AI management system",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,7 +50,8 @@
50
50
  "@chainlesschain/shared-logger": "0.1.0",
51
51
  "@chainlesschain/core-db": "0.1.0",
52
52
  "@chainlesschain/core-config": "0.1.0",
53
- "@chainlesschain/core-infra": "0.1.0"
53
+ "@chainlesschain/core-infra": "0.1.0",
54
+ "ws": "^8.14.2"
54
55
  },
55
56
  "devDependencies": {
56
57
  "vitest": "^3.1.1"
@@ -16,7 +16,11 @@ export function registerAgentCommand(program) {
16
16
  "Start an agentic AI session (reads/writes files, runs commands)",
17
17
  )
18
18
  .option("--model <model>", "Model name", "qwen2:7b")
19
- .option("--provider <provider>", "LLM provider (ollama, openai)", "ollama")
19
+ .option(
20
+ "--provider <provider>",
21
+ "LLM provider (ollama, openai, volcengine, deepseek, ...)",
22
+ "ollama",
23
+ )
20
24
  .option("--base-url <url>", "API base URL")
21
25
  .option("--api-key <key>", "API key")
22
26
  .option("--session <id>", "Resume a previous agent session")
@@ -6,6 +6,7 @@
6
6
  import ora from "ora";
7
7
  import chalk from "chalk";
8
8
  import { logger } from "../lib/logger.js";
9
+ import { BUILT_IN_PROVIDERS } from "../lib/llm-providers.js";
9
10
 
10
11
  /**
11
12
  * Send a single question to an LLM provider
@@ -37,12 +38,24 @@ async function queryLLM(question, options = {}) {
37
38
 
38
39
  const data = await response.json();
39
40
  return data.response;
40
- } else if (provider === "openai") {
41
- const apiKey = options.apiKey || process.env.OPENAI_API_KEY;
41
+ } else {
42
+ // OpenAI-compatible providers (openai, volcengine, deepseek, dashscope, mistral, gemini)
43
+ const providerDef = BUILT_IN_PROVIDERS[provider];
44
+ if (!providerDef) {
45
+ throw new Error(
46
+ `Unsupported provider: ${provider}. Supported: ollama, openai, volcengine, deepseek, dashscope, gemini, mistral, anthropic`,
47
+ );
48
+ }
49
+
50
+ const apiKey =
51
+ options.apiKey ||
52
+ (providerDef.apiKeyEnv ? process.env[providerDef.apiKeyEnv] : null);
42
53
  if (!apiKey)
43
- throw new Error("OpenAI API key required (--api-key or OPENAI_API_KEY)");
54
+ throw new Error(
55
+ `API key required for ${provider} (--api-key or ${providerDef.apiKeyEnv})`,
56
+ );
44
57
 
45
- const apiBase = options.baseUrl || "https://api.openai.com/v1";
58
+ const apiBase = options.baseUrl || providerDef.baseUrl;
46
59
  const response = await fetch(`${apiBase}/chat/completions`, {
47
60
  method: "POST",
48
61
  headers: {
@@ -50,22 +63,20 @@ async function queryLLM(question, options = {}) {
50
63
  Authorization: `Bearer ${apiKey}`,
51
64
  },
52
65
  body: JSON.stringify({
53
- model: model || "gpt-4o-mini",
66
+ model: model || providerDef.models[0],
54
67
  messages: [{ role: "user", content: question }],
55
68
  }),
56
69
  });
57
70
 
58
71
  if (!response.ok) {
59
72
  throw new Error(
60
- `OpenAI error: ${response.status} ${response.statusText}`,
73
+ `${provider} error: ${response.status} ${response.statusText}`,
61
74
  );
62
75
  }
63
76
 
64
77
  const data = await response.json();
65
78
  return data.choices[0].message.content;
66
79
  }
67
-
68
- throw new Error(`Unsupported provider: ${provider}`);
69
80
  }
70
81
 
71
82
  export function registerAskCommand(program) {
@@ -74,7 +85,11 @@ export function registerAskCommand(program) {
74
85
  .description("Ask a question to the AI (single-shot)")
75
86
  .argument("<question>", "The question to ask")
76
87
  .option("--model <model>", "Model name", "qwen2:7b")
77
- .option("--provider <provider>", "LLM provider (ollama, openai)", "ollama")
88
+ .option(
89
+ "--provider <provider>",
90
+ "LLM provider (ollama, openai, volcengine, deepseek, ...)",
91
+ "ollama",
92
+ )
78
93
  .option("--base-url <url>", "API base URL")
79
94
  .option("--api-key <key>", "API key")
80
95
  .option("--json", "Output as JSON")
@@ -11,7 +11,11 @@ export function registerChatCommand(program) {
11
11
  .command("chat")
12
12
  .description("Start an interactive AI chat session")
13
13
  .option("--model <model>", "Model name", "qwen2:7b")
14
- .option("--provider <provider>", "LLM provider (ollama, openai)", "ollama")
14
+ .option(
15
+ "--provider <provider>",
16
+ "LLM provider (ollama, openai, volcengine, deepseek, ...)",
17
+ "ollama",
18
+ )
15
19
  .option("--base-url <url>", "API base URL")
16
20
  .option("--api-key <key>", "API key")
17
21
  .option(
@@ -0,0 +1,266 @@
1
+ /**
2
+ * CLI-Anything commands — discover & register CLI-Anything generated tools
3
+ * chainlesschain cli-anything doctor|scan|register|list|remove
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import { logger } from "../lib/logger.js";
8
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
9
+ import {
10
+ ensureCliAnythingTables,
11
+ detectPython,
12
+ detectCliAnything,
13
+ scanPathForTools,
14
+ parseToolHelp,
15
+ registerTool,
16
+ removeTool,
17
+ listTools,
18
+ } from "../lib/cli-anything-bridge.js";
19
+
20
+ export function registerCliAnythingCommand(program) {
21
+ const cliAny = program
22
+ .command("cli-anything")
23
+ .description(
24
+ "CLI-Anything — discover and register Agent-native CLI tools as skills",
25
+ );
26
+
27
+ /* ---- doctor ---- */
28
+ cliAny
29
+ .command("doctor")
30
+ .description("Check Python & CLI-Anything environment")
31
+ .option("--json", "Output as JSON")
32
+ .action(async (opts) => {
33
+ const py = detectPython();
34
+ const clia = py.found ? detectCliAnything() : { installed: false };
35
+ const tools = scanPathForTools();
36
+
37
+ const report = {
38
+ python: py,
39
+ cliAnything: clia,
40
+ toolsOnPath: tools.length,
41
+ };
42
+
43
+ if (opts.json) {
44
+ console.log(JSON.stringify(report, null, 2));
45
+ return;
46
+ }
47
+
48
+ logger.log("");
49
+ logger.log(chalk.bold(" CLI-Anything Environment"));
50
+ logger.log("");
51
+
52
+ // Python
53
+ if (py.found) {
54
+ logger.log(
55
+ ` ${chalk.green("✓")} Python ${chalk.cyan(py.version)} (${py.command})`,
56
+ );
57
+ } else {
58
+ logger.log(` ${chalk.red("✗")} Python not found`);
59
+ }
60
+
61
+ // CLI-Anything
62
+ if (clia.installed) {
63
+ logger.log(
64
+ ` ${chalk.green("✓")} CLI-Anything ${chalk.cyan(clia.version)}`,
65
+ );
66
+ } else {
67
+ logger.log(` ${chalk.red("✗")} CLI-Anything not installed`);
68
+ if (py.found) {
69
+ logger.log(
70
+ ` ${chalk.gray(`Install: ${py.command} -m pip install cli-anything`)}`,
71
+ );
72
+ }
73
+ }
74
+
75
+ // Tools
76
+ logger.log(
77
+ ` ${tools.length > 0 ? chalk.green("✓") : chalk.yellow("○")} ${tools.length} tool(s) on PATH`,
78
+ );
79
+ for (const t of tools) {
80
+ logger.log(
81
+ ` ${chalk.gray(`cli-anything-${t.name}`)} → ${chalk.gray(t.path)}`,
82
+ );
83
+ }
84
+ logger.log("");
85
+ });
86
+
87
+ /* ---- scan ---- */
88
+ cliAny
89
+ .command("scan")
90
+ .description("Scan PATH for cli-anything-* tools")
91
+ .option("--json", "Output as JSON")
92
+ .action(async (opts) => {
93
+ const tools = scanPathForTools();
94
+
95
+ if (opts.json) {
96
+ console.log(JSON.stringify(tools, null, 2));
97
+ return;
98
+ }
99
+
100
+ if (tools.length === 0) {
101
+ logger.info("No cli-anything-* tools found on PATH.");
102
+ logger.log(
103
+ chalk.gray(
104
+ " Use CLI-Anything to generate tools first: /cli-anything <software>",
105
+ ),
106
+ );
107
+ return;
108
+ }
109
+
110
+ logger.log("");
111
+ logger.log(chalk.bold(` Found ${tools.length} tool(s):`));
112
+ logger.log("");
113
+ for (const t of tools) {
114
+ const help = parseToolHelp(t.command);
115
+ logger.log(` ${chalk.cyan(t.name)}`);
116
+ logger.log(` Command: ${chalk.gray(t.command)}`);
117
+ logger.log(` Path: ${chalk.gray(t.path)}`);
118
+ if (help.description) {
119
+ logger.log(` Desc: ${chalk.gray(help.description)}`);
120
+ }
121
+ if (help.subcommands.length > 0) {
122
+ logger.log(
123
+ ` Subs: ${chalk.gray(help.subcommands.map((s) => s.name).join(", "))}`,
124
+ );
125
+ }
126
+ }
127
+ logger.log("");
128
+ });
129
+
130
+ /* ---- register ---- */
131
+ cliAny
132
+ .command("register <name>")
133
+ .description("Register a cli-anything-* tool as a ChainlessChain skill")
134
+ .option("--force", "Overwrite existing registration")
135
+ .option("--json", "Output as JSON")
136
+ .action(async (name, opts) => {
137
+ try {
138
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
139
+ if (!ctx.db) {
140
+ logger.error(
141
+ "Database not available. Run `chainlesschain setup` first.",
142
+ );
143
+ process.exit(1);
144
+ }
145
+ const db = ctx.db.getDatabase();
146
+ ensureCliAnythingTables(db);
147
+
148
+ const command = `cli-anything-${name}`;
149
+ const helpData = parseToolHelp(command);
150
+
151
+ const result = registerTool(db, name, {
152
+ command,
153
+ helpData,
154
+ force: opts.force,
155
+ });
156
+
157
+ await shutdown();
158
+
159
+ if (opts.json) {
160
+ console.log(JSON.stringify(result, null, 2));
161
+ return;
162
+ }
163
+
164
+ logger.success(
165
+ `Registered ${chalk.cyan(name)} as skill ${chalk.bold(result.skillName)}`,
166
+ );
167
+ logger.log(` Skill dir: ${chalk.gray(result.dir)}`);
168
+ if (result.subcommands.length > 0) {
169
+ logger.log(
170
+ ` Subcommands: ${chalk.gray(result.subcommands.map((s) => s.name).join(", "))}`,
171
+ );
172
+ }
173
+ logger.log(
174
+ chalk.gray(
175
+ ` Use in Agent: /skill ${result.skillName} <subcommand> [args]`,
176
+ ),
177
+ );
178
+ } catch (err) {
179
+ logger.error(`Register failed: ${err.message}`);
180
+ process.exit(1);
181
+ }
182
+ });
183
+
184
+ /* ---- list (default) ---- */
185
+ cliAny
186
+ .command("list", { isDefault: true })
187
+ .description("List registered CLI-Anything tools")
188
+ .option("--json", "Output as JSON")
189
+ .action(async (opts) => {
190
+ try {
191
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
192
+ if (!ctx.db) {
193
+ logger.error("Database not available.");
194
+ process.exit(1);
195
+ }
196
+ const db = ctx.db.getDatabase();
197
+ ensureCliAnythingTables(db);
198
+
199
+ const tools = listTools(db);
200
+ await shutdown();
201
+
202
+ if (opts.json) {
203
+ console.log(JSON.stringify(tools, null, 2));
204
+ return;
205
+ }
206
+
207
+ if (tools.length === 0) {
208
+ logger.info("No CLI-Anything tools registered.");
209
+ logger.log(
210
+ chalk.gray(
211
+ " Run `chainlesschain cli-anything scan` to discover tools.",
212
+ ),
213
+ );
214
+ return;
215
+ }
216
+
217
+ logger.log("");
218
+ logger.log(chalk.bold(` ${tools.length} registered tool(s):`));
219
+ logger.log("");
220
+ for (const t of tools) {
221
+ const statusColor =
222
+ t.status === "registered" ? chalk.green : chalk.yellow;
223
+ logger.log(
224
+ ` ${chalk.cyan(t.name)} ${statusColor(`[${t.status}]`)} → ${chalk.gray(t.skill_name)}`,
225
+ );
226
+ if (t.description) {
227
+ logger.log(` ${chalk.gray(t.description)}`);
228
+ }
229
+ }
230
+ logger.log("");
231
+ } catch (err) {
232
+ logger.error(`List failed: ${err.message}`);
233
+ process.exit(1);
234
+ }
235
+ });
236
+
237
+ /* ---- remove ---- */
238
+ cliAny
239
+ .command("remove <name>")
240
+ .description("Remove a registered CLI-Anything tool")
241
+ .option("--json", "Output as JSON")
242
+ .action(async (name, opts) => {
243
+ try {
244
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
245
+ if (!ctx.db) {
246
+ logger.error("Database not available.");
247
+ process.exit(1);
248
+ }
249
+ const db = ctx.db.getDatabase();
250
+ ensureCliAnythingTables(db);
251
+
252
+ const result = removeTool(db, name);
253
+ await shutdown();
254
+
255
+ if (opts.json) {
256
+ console.log(JSON.stringify(result, null, 2));
257
+ return;
258
+ }
259
+
260
+ logger.success(`Removed tool ${chalk.cyan(name)}`);
261
+ } catch (err) {
262
+ logger.error(`Remove failed: ${err.message}`);
263
+ process.exit(1);
264
+ }
265
+ });
266
+ }