@sloop-dev/skill-forge 0.1.0
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/LICENSE +21 -0
- package/README.md +341 -0
- package/bin/skill-forge.js +2 -0
- package/dist/commands/add.d.ts +5 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +48 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/call.d.ts +13 -0
- package/dist/commands/call.d.ts.map +1 -0
- package/dist/commands/call.js +75 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/gen.d.ts +8 -0
- package/dist/commands/gen.d.ts.map +1 -0
- package/dist/commands/gen.js +128 -0
- package/dist/commands/gen.js.map +1 -0
- package/dist/commands/list.d.ts +9 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +25 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/remove.d.ts +4 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +26 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +64 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config/manager.d.ts +9 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +72 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/schema.d.ts +21 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +2 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/gen/manifest.d.ts +16 -0
- package/dist/gen/manifest.d.ts.map +1 -0
- package/dist/gen/manifest.js +32 -0
- package/dist/gen/manifest.js.map +1 -0
- package/dist/gen/reference-writer.d.ts +19 -0
- package/dist/gen/reference-writer.d.ts.map +1 -0
- package/dist/gen/reference-writer.js +41 -0
- package/dist/gen/reference-writer.js.map +1 -0
- package/dist/gen/skill-writer.d.ts +8 -0
- package/dist/gen/skill-writer.d.ts.map +1 -0
- package/dist/gen/skill-writer.js +19 -0
- package/dist/gen/skill-writer.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/cli-runner.d.ts +8 -0
- package/dist/tools/cli-runner.d.ts.map +1 -0
- package/dist/tools/cli-runner.js +7 -0
- package/dist/tools/cli-runner.js.map +1 -0
- package/dist/tools/help-parser.d.ts +10 -0
- package/dist/tools/help-parser.d.ts.map +1 -0
- package/dist/tools/help-parser.js +18 -0
- package/dist/tools/help-parser.js.map +1 -0
- package/dist/tools/mcp-client.d.ts +21 -0
- package/dist/tools/mcp-client.d.ts.map +1 -0
- package/dist/tools/mcp-client.js +37 -0
- package/dist/tools/mcp-client.js.map +1 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +20 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/process.d.ts +11 -0
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +26 -0
- package/dist/utils/process.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 skill-forge contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# skill-forge
|
|
2
|
+
|
|
3
|
+
**A CLI middleware that bridges AI agents to MCP servers and CLI tools via lightweight skill files.**
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
Modern AI agents rely on tool definitions to interact with the outside world. As the number of tools grows, so does the context window overhead: full MCP schemas, argument descriptions, and usage examples eat into the tokens available for actual reasoning. A project with ten registered tools can easily burn thousands of tokens before the agent even starts working.
|
|
8
|
+
|
|
9
|
+
## The Solution
|
|
10
|
+
|
|
11
|
+
skill-forge sits between your AI agent and its tools. Instead of feeding raw schemas into the context window, it **generates compact Markdown skill files** that summarize each tool's capabilities, triggers, and invocation syntax. At runtime, the agent reads a single `SKILL.md` to discover what is available and calls tools through skill-forge's thin proxy layer.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
SKILL.md
|
|
15
|
+
|
|
|
16
|
+
AI Agent ----> skill-forge ----> MCP Server
|
|
17
|
+
|
|
|
18
|
+
+-------------> CLI Tool
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The result: smaller context windows, faster agent loops, and a single registry for every tool your agent can reach.
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
+------------+ +--------------+ +----------------+
|
|
27
|
+
| | add | | stdio | |
|
|
28
|
+
| AI Agent +------>| skill-forge +------>| MCP Server |
|
|
29
|
+
| | call | | | |
|
|
30
|
+
| | | - registry | +----------------+
|
|
31
|
+
| | | - gen |
|
|
32
|
+
| | | - proxy | +----------------+
|
|
33
|
+
| | | | spawn | |
|
|
34
|
+
| | | +------>| CLI Tool |
|
|
35
|
+
+------------+ +--------------+ +----------------+
|
|
36
|
+
|
|
|
37
|
+
v
|
|
38
|
+
skill-forge/
|
|
39
|
+
SKILL.md
|
|
40
|
+
references/
|
|
41
|
+
tool-a.md
|
|
42
|
+
tool-b.md
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
skill-forge manages a **tool registry** (JSON config), connects to MCP servers via the SDK stdio transport, and spawns CLI tools as child processes. The `gen` command introspects every registered tool and writes a `SKILL.md` plus per-tool reference files that any agent can consume.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install -g skill-forge
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Requirements:** Node.js 18 or later.
|
|
54
|
+
|
|
55
|
+
Verify the installation:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
skill-forge --version
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### 1. Register a tool
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Add an MCP server
|
|
67
|
+
skill-forge add my-server mcp --command npx --args @example/mcp-server
|
|
68
|
+
|
|
69
|
+
# Add a CLI tool
|
|
70
|
+
skill-forge add git-tool cli --command git
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. List registered tools
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
skill-forge list
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
my-server (mcp) — npx
|
|
81
|
+
git-tool (cli) — git
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Generate skill files
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
skill-forge gen
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This creates a `skill-forge/` directory containing `SKILL.md` and a `references/` folder with one file per tool.
|
|
91
|
+
|
|
92
|
+
### 4. Call a tool
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Call an MCP method
|
|
96
|
+
skill-forge call my-server list_files '{"path": "/src"}'
|
|
97
|
+
|
|
98
|
+
# Call a CLI tool
|
|
99
|
+
skill-forge call git-tool "status --short"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Commands
|
|
103
|
+
|
|
104
|
+
| Command | Description |
|
|
105
|
+
| --------- | ------------------------------------------------ |
|
|
106
|
+
| `add` | Register a new MCP or CLI tool |
|
|
107
|
+
| `update` | Update settings for a registered tool |
|
|
108
|
+
| `remove` | Unregister a tool |
|
|
109
|
+
| `list` | Show all registered tools |
|
|
110
|
+
| `gen` | Generate skill files from registered tools |
|
|
111
|
+
| `call` | Invoke a registered MCP method or CLI command |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### `add`
|
|
116
|
+
|
|
117
|
+
Register a new MCP or CLI tool in the config.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
skill-forge add <name> <type> --command <cmd> [--args <items...>] [--env <pairs...>]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Arguments:**
|
|
124
|
+
|
|
125
|
+
- `name` -- Unique identifier for the tool
|
|
126
|
+
- `type` -- `mcp` or `cli`
|
|
127
|
+
|
|
128
|
+
**Options:**
|
|
129
|
+
|
|
130
|
+
- `--command <cmd>` -- Command to execute (required)
|
|
131
|
+
- `--args <items...>` -- Arguments passed to the command (MCP only)
|
|
132
|
+
- `--env <pairs...>` -- Environment variables as `KEY=VALUE` pairs (MCP only)
|
|
133
|
+
|
|
134
|
+
**Examples:**
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
skill-forge add my-server mcp --command npx --args @example/mcp-server
|
|
138
|
+
skill-forge add my-server mcp --command node --args server.js --env API_KEY=abc123
|
|
139
|
+
skill-forge add docker cli --command docker
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### `update`
|
|
145
|
+
|
|
146
|
+
Modify settings for a tool that is already registered.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
skill-forge update <name> [--command <cmd>] [--args <items...>] [--env <pairs...>] [--timeout <ms>]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Options:**
|
|
153
|
+
|
|
154
|
+
- `--command <cmd>` -- Replace the command
|
|
155
|
+
- `--args <items...>` -- Replace arguments (MCP only)
|
|
156
|
+
- `--env <pairs...>` -- Merge environment variables (MCP only)
|
|
157
|
+
- `--timeout <ms>` -- Set a custom timeout in milliseconds
|
|
158
|
+
|
|
159
|
+
**Example:**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
skill-forge update my-server --timeout 60000
|
|
163
|
+
skill-forge update my-server --env API_KEY=new-value
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### `remove`
|
|
169
|
+
|
|
170
|
+
Unregister a tool from the config.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
skill-forge remove <name>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Example:**
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
skill-forge remove my-server
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
### `list`
|
|
185
|
+
|
|
186
|
+
Display all registered tools with their type and command.
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
skill-forge list
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
### `gen`
|
|
195
|
+
|
|
196
|
+
Generate skill files by introspecting every registered tool. For MCP tools, skill-forge connects via stdio and calls `tools/list`. For CLI tools, it runs `--help` and parses the output.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
skill-forge gen [-o, --output <dir>]
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Options:**
|
|
203
|
+
|
|
204
|
+
- `-o, --output <dir>` -- Output directory (defaults to `.`)
|
|
205
|
+
|
|
206
|
+
The command produces:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
<output>/skill-forge/
|
|
210
|
+
SKILL.md # Summary with trigger keywords
|
|
211
|
+
.skill-forge-manifest.json # Tracks config hashes for incremental updates
|
|
212
|
+
references/
|
|
213
|
+
my-server.md # Detailed MCP tool schemas
|
|
214
|
+
docker.md # Parsed CLI help
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
A **manifest file** tracks the config hash of each tool. On subsequent runs, skill-forge detects added, removed, and changed tools so it can clean up stale reference files automatically.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### `call`
|
|
222
|
+
|
|
223
|
+
Invoke a tool through skill-forge's proxy layer.
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# MCP tool — pass the method name and a JSON argument object
|
|
227
|
+
skill-forge call <name> <method> '<json>'
|
|
228
|
+
|
|
229
|
+
# CLI tool — pass the full command string
|
|
230
|
+
skill-forge call <name> "<command>"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Examples:**
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
skill-forge call my-server list_files '{"path": "/src"}'
|
|
237
|
+
skill-forge call docker "ps -a --format json"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
For MCP tools, skill-forge opens a stdio connection, sends the `tools/call` request, prints the JSON result, and disconnects. For CLI tools, it spawns the configured command with the provided arguments and streams stdout/stderr.
|
|
241
|
+
|
|
242
|
+
## Configuration
|
|
243
|
+
|
|
244
|
+
skill-forge stores its configuration at:
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
~/.skill-forge/config.json
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Format:**
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"version": 1,
|
|
255
|
+
"tools": {
|
|
256
|
+
"my-server": {
|
|
257
|
+
"type": "mcp",
|
|
258
|
+
"command": "npx",
|
|
259
|
+
"args": ["@example/mcp-server"],
|
|
260
|
+
"env": {
|
|
261
|
+
"API_KEY": "abc123"
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
"docker": {
|
|
265
|
+
"type": "cli",
|
|
266
|
+
"command": "docker",
|
|
267
|
+
"timeout": 15000
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
"defaults": {
|
|
271
|
+
"timeout": 30000
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Fields:**
|
|
277
|
+
|
|
278
|
+
| Field | Type | Description |
|
|
279
|
+
| ------------------ | -------- | --------------------------------------------------- |
|
|
280
|
+
| `version` | `number` | Config format version (currently `1`) |
|
|
281
|
+
| `tools` | `object` | Map of tool name to tool configuration |
|
|
282
|
+
| `defaults.timeout` | `number` | Default timeout in milliseconds (default: `30000`) |
|
|
283
|
+
|
|
284
|
+
**Per-tool fields:**
|
|
285
|
+
|
|
286
|
+
| Field | Type | Applies to | Description |
|
|
287
|
+
| --------- | ---------- | ---------- | ------------------------------------ |
|
|
288
|
+
| `type` | `string` | all | `"mcp"` or `"cli"` |
|
|
289
|
+
| `command` | `string` | all | Executable to run |
|
|
290
|
+
| `args` | `string[]` | mcp | Arguments passed to the command |
|
|
291
|
+
| `env` | `object` | mcp | Environment variables for the process|
|
|
292
|
+
| `timeout` | `number` | all | Per-tool timeout override (ms) |
|
|
293
|
+
|
|
294
|
+
The config file is created automatically on first use. You can also edit it directly.
|
|
295
|
+
|
|
296
|
+
## How It Works
|
|
297
|
+
|
|
298
|
+
### Thin Proxy Architecture
|
|
299
|
+
|
|
300
|
+
skill-forge does not wrap or reimplement tool protocols. It acts as a **thin proxy**:
|
|
301
|
+
|
|
302
|
+
- **MCP tools** are connected via the official `@modelcontextprotocol/sdk` stdio transport. skill-forge spawns the server process, performs the JSON-RPC handshake, and forwards requests verbatim.
|
|
303
|
+
- **CLI tools** are spawned as child processes with arguments split by whitespace. No shell is invoked, so command strings are executed safely.
|
|
304
|
+
|
|
305
|
+
### Skill Generation
|
|
306
|
+
|
|
307
|
+
The `gen` command introspects each tool and produces Markdown output:
|
|
308
|
+
|
|
309
|
+
1. **MCP tools** -- skill-forge connects, calls `tools/list`, and renders each tool's name, description, parameter schema, and an example invocation.
|
|
310
|
+
2. **CLI tools** -- skill-forge runs `<command> --help`, parses the output into subcommands, and renders each with a description and example.
|
|
311
|
+
|
|
312
|
+
The top-level `SKILL.md` includes YAML front matter with trigger keywords that help agents decide when to use skill-forge.
|
|
313
|
+
|
|
314
|
+
### Manifest Sync
|
|
315
|
+
|
|
316
|
+
A `.skill-forge-manifest.json` file tracks the SHA-256 config hash of each tool. On subsequent `gen` runs, skill-forge compares hashes to detect changes and removes reference files for tools that are no longer registered.
|
|
317
|
+
|
|
318
|
+
## Security
|
|
319
|
+
|
|
320
|
+
skill-forge is designed to be safe by default:
|
|
321
|
+
|
|
322
|
+
- **No shell injection.** CLI tools are spawned with `shell: false`. The command string is split by whitespace into an argument array, preventing shell metacharacter injection.
|
|
323
|
+
- **Config file permissions.** On Unix-like systems, the config file is written with mode `600` (owner read/write only).
|
|
324
|
+
- **No secrets in generated files.** Skill files contain only tool names, descriptions, and usage examples. Environment variables and connection details stay in the config.
|
|
325
|
+
- **Timeouts.** Every tool invocation has a configurable timeout (default 30 seconds). On timeout, the process receives `SIGTERM` followed by `SIGKILL` after a 3-second grace period.
|
|
326
|
+
|
|
327
|
+
## Cross-Platform
|
|
328
|
+
|
|
329
|
+
skill-forge works on **Windows**, **macOS**, and **Linux**. The child process layer uses Node.js `spawn` directly, and the config directory resolves via `os.homedir()` on all platforms.
|
|
330
|
+
|
|
331
|
+
Config location by platform:
|
|
332
|
+
|
|
333
|
+
| Platform | Path |
|
|
334
|
+
| -------- | --------------------------------- |
|
|
335
|
+
| Linux | `~/.skill-forge/config.json` |
|
|
336
|
+
| macOS | `~/.skill-forge/config.json` |
|
|
337
|
+
| Windows | `%USERPROFILE%\.skill-forge\config.json` |
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { ToolConfig } from '../config/schema.js';
|
|
3
|
+
export declare function addTool(name: string, tool: ToolConfig, configPath?: string): Promise<void>;
|
|
4
|
+
export declare function registerAddCommand(program: Command): void;
|
|
5
|
+
//# sourceMappingURL=add.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwCzD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { loadConfig, saveConfig } from '../config/manager.js';
|
|
2
|
+
export async function addTool(name, tool, configPath) {
|
|
3
|
+
const config = await loadConfig(configPath);
|
|
4
|
+
if (config.tools[name]) {
|
|
5
|
+
throw new Error(`Tool '${name}' already exists. Use 'skill-forge update' to modify it.`);
|
|
6
|
+
}
|
|
7
|
+
config.tools[name] = tool;
|
|
8
|
+
await saveConfig(config, configPath);
|
|
9
|
+
}
|
|
10
|
+
export function registerAddCommand(program) {
|
|
11
|
+
program
|
|
12
|
+
.command('add')
|
|
13
|
+
.description('Register a new MCP or CLI tool')
|
|
14
|
+
.argument('<name>', 'Unique tool name')
|
|
15
|
+
.argument('<type>', 'Tool type: mcp or cli')
|
|
16
|
+
.requiredOption('--command <cmd>', 'Command to execute')
|
|
17
|
+
.option('--args <items...>', 'Arguments for the command')
|
|
18
|
+
.option('--env <pairs...>', 'Environment variables as KEY=VALUE pairs')
|
|
19
|
+
.action(async (name, type, opts) => {
|
|
20
|
+
const command = opts.command;
|
|
21
|
+
const args = opts.args;
|
|
22
|
+
const envPairs = opts.env;
|
|
23
|
+
let env;
|
|
24
|
+
if (envPairs) {
|
|
25
|
+
env = {};
|
|
26
|
+
for (const pair of envPairs) {
|
|
27
|
+
const eqIdx = pair.indexOf('=');
|
|
28
|
+
if (eqIdx === -1) {
|
|
29
|
+
console.error(`Invalid env format: "${pair}". Expected KEY=VALUE.`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
env[pair.slice(0, eqIdx)] = pair.slice(eqIdx + 1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const tool = type === 'mcp'
|
|
36
|
+
? { type: 'mcp', command, ...(args && { args }), ...(env && { env }) }
|
|
37
|
+
: { type: 'cli', command };
|
|
38
|
+
try {
|
|
39
|
+
await addTool(name, tool);
|
|
40
|
+
console.log(`Added ${type} tool '${name}'.`);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.error(err.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=add.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAG9D,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAY,EACZ,IAAgB,EAChB,UAAmB;IAEnB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,0DAA0D,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,MAAM,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,gCAAgC,CAAC;SAC7C,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;SACtC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;SAC3C,cAAc,CAAC,iBAAiB,EAAE,oBAAoB,CAAC;SACvD,MAAM,CAAC,mBAAmB,EAAE,2BAA2B,CAAC;SACxD,MAAM,CAAC,kBAAkB,EAAE,0CAA0C,CAAC;SACtE,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,IAAY,EAAE,IAA6B,EAAE,EAAE;QAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,OAAiB,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAA4B,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAA2B,CAAC;QAElD,IAAI,GAAuC,CAAC;QAC5C,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,GAAG,EAAE,CAAC;YACT,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAChC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;oBACjB,OAAO,CAAC,KAAK,CAAC,wBAAwB,IAAI,wBAAwB,CAAC,CAAC;oBACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC;gBACD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GACR,IAAI,KAAK,KAAK;YACZ,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE;YACtE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAE/B,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,UAAU,IAAI,IAAI,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export interface CallToolOptions {
|
|
3
|
+
name: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
configPath?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function callTool(opts: CallToolOptions): Promise<{
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string;
|
|
10
|
+
exitCode: number | null;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function registerCallCommand(program: Command): void;
|
|
13
|
+
//# sourceMappingURL=call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/commands/call.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAuD1H;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiB1D"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { loadConfig } from '../config/manager.js';
|
|
2
|
+
import { McpClient } from '../tools/mcp-client.js';
|
|
3
|
+
import { runCliTool } from '../tools/cli-runner.js';
|
|
4
|
+
export async function callTool(opts) {
|
|
5
|
+
const config = await loadConfig(opts.configPath);
|
|
6
|
+
const tool = config.tools[opts.name];
|
|
7
|
+
if (!tool) {
|
|
8
|
+
throw new Error(`'${opts.name}' is not registered. Run 'skill-forge list' to see available tools.`);
|
|
9
|
+
}
|
|
10
|
+
const timeout = tool.timeout ?? config.defaults.timeout;
|
|
11
|
+
if (tool.type === 'mcp') {
|
|
12
|
+
// MCP: expect 2 args — method + json
|
|
13
|
+
if (opts.args.length !== 2) {
|
|
14
|
+
throw new Error(`'${opts.name}' is an MCP tool. Usage: skill-forge call ${opts.name} <method> '<json>'`);
|
|
15
|
+
}
|
|
16
|
+
const [method, jsonStr] = opts.args;
|
|
17
|
+
const args = JSON.parse(jsonStr);
|
|
18
|
+
const client = new McpClient({
|
|
19
|
+
command: tool.command,
|
|
20
|
+
args: tool.args ?? [],
|
|
21
|
+
env: tool.env,
|
|
22
|
+
timeout,
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
await client.connect();
|
|
26
|
+
const result = await client.callTool(method, args);
|
|
27
|
+
await client.disconnect();
|
|
28
|
+
return { stdout: JSON.stringify(result, null, 2), stderr: '', exitCode: 0 };
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
await client.disconnect().catch(() => { });
|
|
32
|
+
// Wrap timeout errors to include the tool name
|
|
33
|
+
const msg = err.message;
|
|
34
|
+
if (msg.includes('timed out')) {
|
|
35
|
+
throw new Error(`'${opts.name}' timed out after ${timeout}ms`);
|
|
36
|
+
}
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// CLI: expect 1 arg — command string
|
|
42
|
+
if (opts.args.length !== 1) {
|
|
43
|
+
throw new Error(`'${opts.name}' is a CLI tool. Usage: skill-forge call ${opts.name} "<command>"`);
|
|
44
|
+
}
|
|
45
|
+
const result = await runCliTool({
|
|
46
|
+
command: tool.command,
|
|
47
|
+
commandString: opts.args[0],
|
|
48
|
+
timeout,
|
|
49
|
+
});
|
|
50
|
+
// Pass through CLI output as-is, forward exit code
|
|
51
|
+
return { stdout: result.stdout, stderr: result.stderr, exitCode: result.exitCode };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function registerCallCommand(program) {
|
|
55
|
+
program
|
|
56
|
+
.command('call')
|
|
57
|
+
.description('Invoke a registered MCP method or CLI command')
|
|
58
|
+
.argument('<name>', 'Tool name')
|
|
59
|
+
.argument('<args...>', 'For MCP: <method> <json>. For CLI: "<command>"')
|
|
60
|
+
.action(async (name, args) => {
|
|
61
|
+
try {
|
|
62
|
+
const result = await callTool({ name, args });
|
|
63
|
+
if (result.stdout)
|
|
64
|
+
process.stdout.write(result.stdout);
|
|
65
|
+
if (result.stderr)
|
|
66
|
+
process.stderr.write(result.stderr);
|
|
67
|
+
process.exit(result.exitCode ?? 0);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.js","sourceRoot":"","sources":["../../src/commands/call.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export interface GenerateOptions {
|
|
3
|
+
output: string;
|
|
4
|
+
configPath?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function generateSkills(opts: GenerateOptions): Promise<void>;
|
|
7
|
+
export declare function registerGenCommand(program: Command): void;
|
|
8
|
+
//# sourceMappingURL=gen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gen.d.ts","sourceRoot":"","sources":["../../src/commands/gen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAazC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,wBAAsB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDzE;AA+DD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAczD"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { loadConfig } from '../config/manager.js';
|
|
4
|
+
import { McpClient } from '../tools/mcp-client.js';
|
|
5
|
+
import { runCliTool } from '../tools/cli-runner.js';
|
|
6
|
+
import { parseHelpOutput } from '../tools/help-parser.js';
|
|
7
|
+
import { generateSkillMd } from '../gen/skill-writer.js';
|
|
8
|
+
import { generateMcpReference, generateCliReference } from '../gen/reference-writer.js';
|
|
9
|
+
import { loadManifest, saveManifest, diffManifest } from '../gen/manifest.js';
|
|
10
|
+
import { configHash } from '../utils/hash.js';
|
|
11
|
+
// Generate skill files for all registered tools.
|
|
12
|
+
export async function generateSkills(opts) {
|
|
13
|
+
const config = await loadConfig(opts.configPath);
|
|
14
|
+
const skillDir = join(opts.output, 'skill-forge');
|
|
15
|
+
const refsDir = join(skillDir, 'references');
|
|
16
|
+
await mkdir(refsDir, { recursive: true });
|
|
17
|
+
const toolInfos = [];
|
|
18
|
+
const currentHashes = {};
|
|
19
|
+
for (const [name, tool] of Object.entries(config.tools)) {
|
|
20
|
+
const hash = configHash(tool);
|
|
21
|
+
currentHashes[name] = hash;
|
|
22
|
+
let refMd;
|
|
23
|
+
let description;
|
|
24
|
+
let triggers;
|
|
25
|
+
if (tool.type === 'mcp') {
|
|
26
|
+
const result = await generateMcpToolReference(name, tool);
|
|
27
|
+
refMd = result.refMd;
|
|
28
|
+
description = result.description;
|
|
29
|
+
triggers = result.triggers;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const result = await generateCliToolReference(name, tool);
|
|
33
|
+
refMd = result.refMd;
|
|
34
|
+
description = result.description;
|
|
35
|
+
triggers = result.triggers;
|
|
36
|
+
}
|
|
37
|
+
await writeFile(join(refsDir, `${name}.md`), refMd, 'utf-8');
|
|
38
|
+
toolInfos.push({ name, type: tool.type, description, triggers });
|
|
39
|
+
}
|
|
40
|
+
// Generate SKILL.md
|
|
41
|
+
const skillMd = generateSkillMd(toolInfos);
|
|
42
|
+
await writeFile(join(skillDir, 'SKILL.md'), skillMd, 'utf-8');
|
|
43
|
+
// Load previous manifest, diff, and clean up removed tool reference files
|
|
44
|
+
const previousManifest = await loadManifest(skillDir);
|
|
45
|
+
const diff = diffManifest(previousManifest, currentHashes);
|
|
46
|
+
for (const removedName of diff.removed) {
|
|
47
|
+
await rm(join(refsDir, `${removedName}.md`), { force: true });
|
|
48
|
+
}
|
|
49
|
+
// Save new manifest
|
|
50
|
+
const newManifest = {
|
|
51
|
+
generated_at: new Date().toISOString(),
|
|
52
|
+
tools: {},
|
|
53
|
+
};
|
|
54
|
+
for (const [name, hash] of Object.entries(currentHashes)) {
|
|
55
|
+
newManifest.tools[name] = { config_hash: hash };
|
|
56
|
+
}
|
|
57
|
+
await saveManifest(skillDir, newManifest);
|
|
58
|
+
}
|
|
59
|
+
// Generate reference for an MCP tool by connecting and listing tools.
|
|
60
|
+
async function generateMcpToolReference(name, tool) {
|
|
61
|
+
try {
|
|
62
|
+
const client = new McpClient({
|
|
63
|
+
command: tool.command,
|
|
64
|
+
args: tool.args ?? [],
|
|
65
|
+
env: tool.env,
|
|
66
|
+
timeout: tool.timeout ?? 30000,
|
|
67
|
+
});
|
|
68
|
+
await client.connect();
|
|
69
|
+
const tools = await client.listTools();
|
|
70
|
+
await client.disconnect();
|
|
71
|
+
const schemas = tools.map(t => ({
|
|
72
|
+
name: t.name,
|
|
73
|
+
description: t.description,
|
|
74
|
+
inputSchema: t.inputSchema,
|
|
75
|
+
}));
|
|
76
|
+
const refMd = generateMcpReference(name, schemas);
|
|
77
|
+
const description = schemas.length > 0
|
|
78
|
+
? schemas.map(s => s.description || s.name).join(', ')
|
|
79
|
+
: `MCP tool: ${name}`;
|
|
80
|
+
const triggers = [name, ...schemas.map(s => s.name)];
|
|
81
|
+
return { refMd, description, triggers };
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Fallback when MCP connection fails
|
|
85
|
+
const refMd = `# ${name}\n\nMCP server could not be reached during generation.\nRun \`skill-forge call ${name} <method> '<json>'\` to use.\n`;
|
|
86
|
+
return { refMd, description: `MCP tool: ${name}`, triggers: [name] };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Generate reference for a CLI tool by running --help and parsing the output.
|
|
90
|
+
async function generateCliToolReference(name, tool) {
|
|
91
|
+
try {
|
|
92
|
+
const result = await runCliTool({
|
|
93
|
+
command: tool.command,
|
|
94
|
+
commandString: '--help',
|
|
95
|
+
timeout: tool.timeout ?? 10000,
|
|
96
|
+
});
|
|
97
|
+
const helpOutput = result.stdout || result.stderr;
|
|
98
|
+
const parsed = parseHelpOutput(helpOutput);
|
|
99
|
+
const refMd = generateCliReference(name, parsed.commands, parsed.raw);
|
|
100
|
+
const description = parsed.commands.length > 0
|
|
101
|
+
? parsed.commands.map(c => c.description).join(', ')
|
|
102
|
+
: `CLI tool: ${name}`;
|
|
103
|
+
const triggers = [name, ...parsed.commands.map(c => c.name)];
|
|
104
|
+
return { refMd, description, triggers };
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Fallback when help parse fails
|
|
108
|
+
const refMd = generateCliReference(name, [], '');
|
|
109
|
+
return { refMd, description: `CLI tool: ${name}`, triggers: [name] };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function registerGenCommand(program) {
|
|
113
|
+
program
|
|
114
|
+
.command('gen')
|
|
115
|
+
.description('Generate skill files from registered tools')
|
|
116
|
+
.option('-o, --output <dir>', 'Output directory', '.')
|
|
117
|
+
.action(async (opts) => {
|
|
118
|
+
try {
|
|
119
|
+
await generateSkills({ output: opts.output });
|
|
120
|
+
console.log('Skill files generated successfully.');
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
console.error(`Generation failed: ${err.message}`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=gen.js.map
|