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 +210 -17
- package/package.json +3 -2
- package/src/commands/agent.js +5 -1
- package/src/commands/ask.js +24 -9
- package/src/commands/chat.js +5 -1
- package/src/commands/cli-anything.js +266 -0
- package/src/commands/serve.js +109 -0
- package/src/constants.js +1 -0
- package/src/index.js +12 -0
- package/src/lib/cli-anything-bridge.js +379 -0
- package/src/lib/git-integration.js +1 -1
- package/src/lib/llm-providers.js +14 -1
- package/src/lib/task-model-selector.js +232 -0
- package/src/lib/ws-server.js +474 -0
- package/src/repl/agent-repl.js +25 -4
- package/src/repl/chat-repl.js +14 -6
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
|
|
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
|
|
686
|
-
|
|
|
687
|
-
| Ollama (Local)
|
|
688
|
-
| OpenAI
|
|
689
|
-
| Anthropic
|
|
690
|
-
| DashScope (Alibaba)
|
|
691
|
-
| DeepSeek
|
|
692
|
-
| Gemini (Google)
|
|
693
|
-
| Mistral
|
|
694
|
-
|
|
|
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 (
|
|
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 |
|
|
724
|
-
| Unit — commands |
|
|
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 |
|
|
727
|
-
| E2E |
|
|
919
|
+
| Integration | 5 | 30+ | All passing |
|
|
920
|
+
| E2E | 14 | 150+ | All passing |
|
|
728
921
|
| Core packages (external) | — | 118 | All passing |
|
|
729
|
-
| **CLI Total** | **
|
|
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.
|
|
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"
|
package/src/commands/agent.js
CHANGED
|
@@ -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(
|
|
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")
|
package/src/commands/ask.js
CHANGED
|
@@ -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
|
|
41
|
-
|
|
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(
|
|
54
|
+
throw new Error(
|
|
55
|
+
`API key required for ${provider} (--api-key or ${providerDef.apiKeyEnv})`,
|
|
56
|
+
);
|
|
44
57
|
|
|
45
|
-
const apiBase = options.baseUrl ||
|
|
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 ||
|
|
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
|
-
|
|
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(
|
|
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")
|
package/src/commands/chat.js
CHANGED
|
@@ -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(
|
|
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
|
+
}
|