toolcapsule 0.1.0-alpha.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ToolCapsule 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,150 @@
1
+ # ToolCapsule
2
+
3
+ > Heavy MCP tools don't belong in your prompt. Put them in a ToolCapsule.
4
+
5
+ **ToolCapsule** turns heavy MCP servers into lightweight, lazy-loaded, file-first **Agent Skills** with patch-and-retry workflows.
6
+
7
+ It is not a replacement for MCP or Skills. It is the missing workflow layer between them:
8
+
9
+ ```text
10
+ Heavy MCP server
11
+ → compact Agent Skill
12
+ → local args/content files
13
+ → auditable tool runs
14
+ → patch-and-retry recovery
15
+ ```
16
+
17
+ ## Why ToolCapsule
18
+
19
+ MCP is becoming the standard way to connect agents to tools. Skills are becoming the standard way to package agent workflows.
20
+
21
+ But large MCP servers can be expensive in agent contexts:
22
+
23
+ - long tool descriptions and schemas may be visible every turn;
24
+ - many turns do not need a real tool call;
25
+ - document/workflow tools often carry large Markdown or JSON payloads;
26
+ - failed tool calls force the model to regenerate the whole call.
27
+
28
+ ToolCapsule keeps the MCP server as the source of truth, but exposes it through a lightweight Skill and local artifacts.
29
+
30
+ ## What is a ToolCapsule?
31
+
32
+ A ToolCapsule is a file-first tool call package:
33
+
34
+ ```text
35
+ capsule = MCP tool + args.json + payload files + run metadata + retry history
36
+ ```
37
+
38
+ Instead of making the model hold everything in the prompt, ToolCapsule stores heavy payloads and run state on disk.
39
+
40
+ ## Quick start
41
+
42
+ ```bash
43
+ npm i -g toolcapsule
44
+
45
+ toolcapsule init feishu --url https://mcp.example.com/mcp/xxx
46
+ toolcapsule tools feishu --brief
47
+ toolcapsule describe feishu create-doc --brief
48
+ toolcapsule call feishu create-doc @args.json --save-run
49
+ toolcapsule retry runs/2026-06-06T10-00-00-000Z
50
+ ```
51
+
52
+ Short alias:
53
+
54
+ ```bash
55
+ tcap tools feishu --brief
56
+ tcap call feishu create-doc @args.json --save-run
57
+ ```
58
+
59
+ ## What it generates
60
+
61
+ ```text
62
+ .github/skills/feishu-mcp/
63
+ SKILL.md # lightweight Agent Skill entrypoint
64
+ toolcapsule.config.json # MCP transport/profile config
65
+ scripts/README.md
66
+ runs/ # auditable tool call records
67
+ ```
68
+
69
+ This lets your agent discover the Skill instead of carrying the full MCP schema in every turn.
70
+
71
+ ## Native MCP vs ToolCapsule
72
+
73
+ | Concern | Native MCP | ToolCapsule |
74
+ |---|---|---|
75
+ | Tool schemas | Often visible every turn | Loaded on demand |
76
+ | Agent workflow | Direct tool call | Skill-guided workflow |
77
+ | Large payloads | Generated inside tool calls | Stored as files |
78
+ | Failed calls | Regenerate full tool call | Patch local file and retry |
79
+ | Auditability | Host dependent | Run directory with request/response/error |
80
+ | Best for | Small, high-frequency tools | Heavy SaaS/document/workflow MCPs |
81
+
82
+ ## Patch-and-retry
83
+
84
+ When a large MCP call fails, do not ask the model to rewrite the whole tool call.
85
+
86
+ ```bash
87
+ # 1. Call with a local args file
88
+ toolcapsule call feishu create-doc @args.json --save-run
89
+
90
+ # 2. Patch args.json or content.md
91
+
92
+ # 3. Retry deterministically
93
+ toolcapsule retry runs/2026-06-06T10-00-00-000Z
94
+ ```
95
+
96
+ This is especially useful for MCP tools that create documents, tickets, pages, dashboards, reports, or batch operations.
97
+
98
+ ## Benchmark result
99
+
100
+ In a Feishu MCP benchmark:
101
+
102
+ - **88.4% fewer input tokens**;
103
+ - **87.6% fewer total tokens**;
104
+ - planning success rate stayed at **100%**.
105
+
106
+ Results vary by MCP server, model, and host.
107
+
108
+ ## CLI
109
+
110
+ ```text
111
+ toolcapsule init <name> --url <remote-mcp-url>
112
+ toolcapsule init <name> --command <stdio-command> --arg <arg>
113
+ toolcapsule tools <profile> --brief
114
+ toolcapsule describe <profile> <tool> --brief
115
+ toolcapsule call <profile> <tool> @args.json --save-run
116
+ toolcapsule retry <run-dir>
117
+ toolcapsule summarize <profile>
118
+ toolcapsule benchmark <profile>
119
+ ```
120
+
121
+ `tcap` is an equivalent short alias for `toolcapsule`.
122
+
123
+ ## Who is it for?
124
+
125
+ ToolCapsule is useful when your MCP server is:
126
+
127
+ - schema-heavy;
128
+ - document/workflow oriented;
129
+ - occasionally used rather than called every turn;
130
+ - carrying large payloads;
131
+ - hard to retry after failures.
132
+
133
+ Examples: Feishu/Lark, Notion, Jira, Linear, Confluence, Google Docs, GitHub management tools, internal SaaS MCPs.
134
+
135
+ ## Project status
136
+
137
+ Early alpha. APIs may change before v1.0.
138
+
139
+ ## Documentation
140
+
141
+ - [Concept](docs/concept.md)
142
+ - [Architecture](docs/architecture.md)
143
+ - [Patch and retry](docs/patch-and-retry.md)
144
+ - [Benchmark methodology](docs/benchmark-methodology.md)
145
+ - [Release checklist](docs/release-checklist.md)
146
+ - [Roadmap](ROADMAP.md)
147
+
148
+ ## License
149
+
150
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,420 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { readFile as readFile3 } from "fs/promises";
5
+ import { join as join4 } from "path";
6
+ import { cac } from "cac";
7
+ import pc from "picocolors";
8
+
9
+ // src/mcp/client.ts
10
+ import { spawn } from "child_process";
11
+ import { once } from "events";
12
+ var McpClient = class {
13
+ child;
14
+ nextId = 1;
15
+ buffer = "";
16
+ pending = /* @__PURE__ */ new Map();
17
+ timeoutMs;
18
+ constructor(profile, opts = {}) {
19
+ this.timeoutMs = opts.timeoutMs ?? Number(process.env.TOOLCAPSULE_TIMEOUT_MS || "45000");
20
+ if (profile.transport.type === "remote") {
21
+ this.child = spawn("npx", ["-y", "mcp-remote", profile.transport.url], {
22
+ stdio: ["pipe", "pipe", "pipe"]
23
+ });
24
+ } else {
25
+ this.child = spawn(profile.transport.command, profile.transport.args ?? [], {
26
+ stdio: ["pipe", "pipe", "pipe"]
27
+ });
28
+ }
29
+ this.child.stdout.setEncoding("utf8");
30
+ this.child.stdout.on("data", (chunk) => this.onStdout(chunk));
31
+ this.child.stderr.on("data", (chunk) => process.stderr.write(chunk));
32
+ this.child.on("exit", (code, signal) => {
33
+ const error = new Error(`MCP process exited early (code=${code}, signal=${signal})`);
34
+ for (const waiter of this.pending.values()) waiter.reject(error);
35
+ this.pending.clear();
36
+ });
37
+ }
38
+ onStdout(chunk) {
39
+ this.buffer += chunk;
40
+ let newlineIndex;
41
+ while ((newlineIndex = this.buffer.indexOf("\n")) >= 0) {
42
+ const line = this.buffer.slice(0, newlineIndex).trim();
43
+ this.buffer = this.buffer.slice(newlineIndex + 1);
44
+ if (!line) continue;
45
+ let message;
46
+ try {
47
+ message = JSON.parse(line);
48
+ } catch {
49
+ process.stderr.write(`${line}
50
+ `);
51
+ continue;
52
+ }
53
+ if (typeof message.id === "number") {
54
+ const waiter = this.pending.get(message.id);
55
+ if (waiter) {
56
+ this.pending.delete(message.id);
57
+ waiter.resolve(message);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ async init() {
63
+ await this.request("initialize", {
64
+ protocolVersion: "2025-03-26",
65
+ capabilities: {},
66
+ clientInfo: { name: "toolcapsule", version: "0.1.0" }
67
+ });
68
+ this.notify("notifications/initialized", {});
69
+ }
70
+ async request(method, params) {
71
+ const id = this.nextId++;
72
+ const responsePromise = new Promise((resolve2, reject) => {
73
+ this.pending.set(id, { resolve: resolve2, reject });
74
+ });
75
+ this.child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
76
+ `);
77
+ const response = await this.withTimeout(responsePromise, method);
78
+ if (response.error) throw new Error(`${method} failed: ${JSON.stringify(response.error, null, 2)}`);
79
+ return response.result;
80
+ }
81
+ notify(method, params) {
82
+ this.child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}
83
+ `);
84
+ }
85
+ async listTools() {
86
+ return await this.request("tools/list", {});
87
+ }
88
+ async callTool(name, args) {
89
+ return await this.request("tools/call", { name, arguments: args });
90
+ }
91
+ async close() {
92
+ if (!this.child.killed) this.child.kill();
93
+ if (this.child.exitCode === null) await Promise.race([once(this.child, "exit"), new Promise((resolve2) => setTimeout(resolve2, 500))]);
94
+ }
95
+ async withTimeout(promise, label) {
96
+ let timer;
97
+ const timeout = new Promise((_, reject) => {
98
+ timer = setTimeout(() => reject(new Error(`${label} timed out after ${this.timeoutMs}ms`)), this.timeoutMs);
99
+ });
100
+ try {
101
+ return await Promise.race([promise, timeout]);
102
+ } finally {
103
+ if (timer) clearTimeout(timer);
104
+ }
105
+ }
106
+ };
107
+
108
+ // src/profile.ts
109
+ import { existsSync } from "fs";
110
+ import { join } from "path";
111
+ import { z } from "zod";
112
+
113
+ // src/utils/fs.ts
114
+ import { mkdir, readFile, writeFile } from "fs/promises";
115
+ import { dirname, resolve } from "path";
116
+ async function readJson(path) {
117
+ return JSON.parse(await readFile(path, "utf8"));
118
+ }
119
+ async function writeJson(path, value) {
120
+ await mkdir(dirname(path), { recursive: true });
121
+ await writeFile(path, `${JSON.stringify(value, null, 2)}
122
+ `);
123
+ }
124
+
125
+ // src/profile.ts
126
+ var profileSchema = z.object({
127
+ name: z.string().min(1),
128
+ transport: z.union([
129
+ z.object({ type: z.literal("remote"), url: z.string().url() }),
130
+ z.object({ type: z.literal("stdio"), command: z.string().min(1), args: z.array(z.string()).optional() })
131
+ ]),
132
+ skill: z.object({
133
+ name: z.string().optional(),
134
+ description: z.string().optional()
135
+ }).optional(),
136
+ shortcuts: z.record(
137
+ z.object({
138
+ tool: z.string(),
139
+ description: z.string().optional(),
140
+ args: z.record(z.enum(["string", "file", "json", "boolean", "number"])).optional()
141
+ })
142
+ ).optional()
143
+ });
144
+ async function loadProfile(profilePathOrName) {
145
+ const candidates = [
146
+ profilePathOrName,
147
+ `${profilePathOrName}.json`,
148
+ join(".toolcapsule", "profiles", `${profilePathOrName}.json`),
149
+ join(".github", "skills", `${profilePathOrName}-mcp`, "toolcapsule.config.json")
150
+ ];
151
+ const found = candidates.find((path) => existsSync(path));
152
+ if (!found) throw new Error(`Profile not found: ${profilePathOrName}`);
153
+ return profileSchema.parse(await readJson(found));
154
+ }
155
+
156
+ // src/schema/brief.ts
157
+ function toolTitle(tool) {
158
+ return tool.annotations?.title || tool.description?.split(/\n/)[0]?.slice(0, 100) || tool.name;
159
+ }
160
+ function briefTool(tool) {
161
+ const schema = tool.inputSchema;
162
+ const required = Array.isArray(schema?.required) ? schema.required : [];
163
+ const properties = schema?.properties && typeof schema.properties === "object" ? Object.keys(schema.properties) : [];
164
+ const flags = [
165
+ tool.annotations?.readOnlyHint ? "read-only" : void 0,
166
+ tool.annotations?.destructiveHint ? "destructive" : void 0
167
+ ].filter(Boolean);
168
+ return `- ${tool.name}: ${toolTitle(tool)}${flags.length ? ` [${flags.join(", ")}]` : ""}${required.length ? ` required=${required.join("|")}` : ""}${properties.length ? ` args=${properties.slice(0, 12).join("|")}` : ""}`;
169
+ }
170
+ function briefTools(tools) {
171
+ return tools.map(briefTool).join("\n");
172
+ }
173
+
174
+ // src/schema/summarize.ts
175
+ function summarizeTool(tool) {
176
+ const schema = tool.inputSchema;
177
+ const properties = schema?.properties && typeof schema.properties === "object" ? schema.properties : {};
178
+ return {
179
+ name: tool.name,
180
+ title: tool.annotations?.title,
181
+ description: tool.description?.split(/\n\n/)[0]?.slice(0, 500),
182
+ annotations: tool.annotations,
183
+ required: Array.isArray(schema?.required) ? schema.required : [],
184
+ arguments: Object.fromEntries(
185
+ Object.entries(properties).map(([key, value]) => [
186
+ key,
187
+ { type: value?.type, enum: value?.enum, description: value?.description?.slice?.(0, 160) }
188
+ ])
189
+ )
190
+ };
191
+ }
192
+ function summarizeTools(tools) {
193
+ return tools.map(summarizeTool);
194
+ }
195
+
196
+ // src/skill/generator.ts
197
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
198
+ import { dirname as dirname2, join as join2 } from "path";
199
+ import Handlebars from "handlebars";
200
+ var skillTemplate = `---
201
+ name: {{skillName}}
202
+ description: '{{description}}'
203
+ argument-hint: 'MCP task, tool name, or args file to run'
204
+ ---
205
+
206
+ # {{title}}
207
+
208
+ This skill wraps an MCP server as a lightweight, lazy-loaded, file-first workflow.
209
+
210
+ ## Why use this skill
211
+
212
+ - Keep full MCP tool schemas out of the model context until needed.
213
+ - Use brief tool summaries for everyday work.
214
+ - Store large payloads in local files such as \`args.json\` or \`content.md\`.
215
+ - Patch local artifacts and retry failed calls deterministically.
216
+
217
+ ## Commands
218
+
219
+ \`\`\`bash
220
+ toolcapsule tools {{profileName}} --brief
221
+ toolcapsule describe {{profileName}} <tool>
222
+ toolcapsule call {{profileName}} <tool> @args.json
223
+ toolcapsule retry <run-dir>
224
+ \`\`\`
225
+
226
+ ## Workflow
227
+
228
+ 1. Use \`tools --brief\` to find the likely tool.
229
+ 2. Use \`describe <tool>\` only when the brief schema is insufficient.
230
+ 3. Put complex arguments in a local JSON file.
231
+ 4. Call with \`@args.json\` and save the run.
232
+ 5. On failure, patch the local file and run \`retry\`.
233
+
234
+ ## Safety
235
+
236
+ - Do not fabricate IDs, URLs, user IDs, or opaque tokens.
237
+ - Prefer local files for large payloads.
238
+ - Do not print secrets.
239
+ - Review destructive tools before calling.
240
+ `;
241
+ async function generateSkill(profile, opts = {}) {
242
+ const skillName = profile.skill?.name || `${profile.name}-mcp`;
243
+ const outputDir = opts.outputDir || join2(".github", "skills", skillName);
244
+ await mkdir2(join2(outputDir, "scripts"), { recursive: true });
245
+ await mkdir2(join2(outputDir, "runs"), { recursive: true });
246
+ const template = Handlebars.compile(skillTemplate);
247
+ const description = profile.skill?.description || `Use when operating tools from the ${profile.name} MCP server. Lazy-load schemas, call tools through local files, and retry failed calls by patching artifacts.`;
248
+ const markdown = template({
249
+ skillName,
250
+ profileName: profile.name,
251
+ title: `${profile.name} MCP Skill`,
252
+ description: description.replace(/'/g, "''")
253
+ });
254
+ await writeFile2(join2(outputDir, "SKILL.md"), markdown);
255
+ await writeJson(join2(outputDir, "toolcapsule.config.json"), profile);
256
+ await writeFile2(
257
+ join2(outputDir, "scripts", "README.md"),
258
+ `# Scripts
259
+
260
+ This skill uses the project-level \`toolcapsule\` CLI.
261
+ `
262
+ );
263
+ return outputDir;
264
+ }
265
+ async function writeProfile(path, profile) {
266
+ await mkdir2(dirname2(path), { recursive: true });
267
+ await writeJson(path, profile);
268
+ }
269
+
270
+ // src/runs/recorder.ts
271
+ import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
272
+ import { join as join3 } from "path";
273
+ function createRunId() {
274
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
275
+ }
276
+ async function saveRun(baseDir, record) {
277
+ const dir = join3(baseDir, record.id);
278
+ await mkdir3(dir, { recursive: true });
279
+ await writeJson(join3(dir, "run.json"), record);
280
+ await writeJson(join3(dir, "request.json"), record.request);
281
+ if (record.response !== void 0) await writeJson(join3(dir, "response.json"), record.response);
282
+ if (record.error) await writeFile3(join3(dir, "error.txt"), record.error);
283
+ await writeFile3(join3(dir, "command.txt"), `${record.command}
284
+ `);
285
+ return dir;
286
+ }
287
+ async function loadRun(runDir) {
288
+ return JSON.parse(await readFile2(join3(runDir, "run.json"), "utf8"));
289
+ }
290
+
291
+ // src/utils/tokens.ts
292
+ function roughTokens(value) {
293
+ const text = typeof value === "string" ? value : JSON.stringify(value);
294
+ return Math.ceil(text.length / 4);
295
+ }
296
+ function percentReduction(before, after) {
297
+ return before === 0 ? 0 : (before - after) / before * 100;
298
+ }
299
+
300
+ // src/cli.ts
301
+ var cli = cac("toolcapsule");
302
+ async function withClient(profile, fn) {
303
+ const client = new McpClient(profile);
304
+ try {
305
+ await client.init();
306
+ return await fn(client);
307
+ } finally {
308
+ await client.close();
309
+ }
310
+ }
311
+ function readArgsPath(raw) {
312
+ return raw.startsWith("@") ? raw.slice(1) : raw;
313
+ }
314
+ cli.command("init <name>", "Create a profile and generated Agent Skill for an MCP server").option("--url <url>", "Remote MCP URL").option("--command <command>", "stdio MCP command").option("--arg <arg>", "stdio MCP argument, repeatable", { type: [String] }).option("--output <dir>", "Skill output directory").action(async (name, options) => {
315
+ if (!options.url && !options.command) throw new Error("Provide --url for remote MCP or --command for stdio MCP");
316
+ const profile = options.url ? { name, transport: { type: "remote", url: options.url } } : { name, transport: { type: "stdio", command: options.command, args: options.arg ?? [] } };
317
+ await writeProfile(join4(".toolcapsule", "profiles", `${name}.json`), profile);
318
+ const out = await generateSkill(profile, options.output ? { outputDir: options.output } : {});
319
+ console.log(pc.green(`Created profile and skill at ${out}`));
320
+ });
321
+ cli.command("tools <profile>", "List MCP tools").option("--brief", "Print compact tool summaries").option("--names", "Print tool names only").option("--json", "Print raw JSON").action(async (profileName, options) => {
322
+ const profile = await loadProfile(profileName);
323
+ const result = await withClient(profile, (client) => client.listTools());
324
+ if (options.json) console.log(JSON.stringify(result, null, 2));
325
+ else if (options.names) console.log(result.tools.map((tool) => tool.name).join("\n"));
326
+ else console.log(briefTools(result.tools));
327
+ });
328
+ cli.command("describe <profile> <tool>", "Describe one MCP tool").option("--brief", "Print summarized schema instead of raw schema").action(async (profileName, toolName, options) => {
329
+ const profile = await loadProfile(profileName);
330
+ const result = await withClient(profile, (client) => client.listTools());
331
+ const tool = result.tools.find((item) => item.name === toolName);
332
+ if (!tool) throw new Error(`Tool not found: ${toolName}`);
333
+ console.log(JSON.stringify(options.brief ? summarizeTool(tool) : tool, null, 2));
334
+ });
335
+ cli.command("call <profile> <tool> <args>", "Call an MCP tool with JSON args or @args.json").option("--save-run", "Save request, response, and command under runs/").action(async (profileName, toolName, argsRaw, options) => {
336
+ const argsPath = readArgsPath(argsRaw);
337
+ const toolArgs = argsRaw.startsWith("@") ? await readJson(argsPath) : JSON.parse(argsRaw);
338
+ const profile = await loadProfile(profileName);
339
+ const request = { name: toolName, arguments: toolArgs };
340
+ const command = `toolcapsule call ${profileName} ${toolName} ${argsRaw}`;
341
+ const runId = createRunId();
342
+ try {
343
+ const response = await withClient(profile, (client) => client.callTool(toolName, toolArgs));
344
+ console.log(JSON.stringify(response, null, 2));
345
+ if (options.saveRun) {
346
+ const dir = await saveRun("runs", {
347
+ id: runId,
348
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
349
+ profile: profileName,
350
+ tool: toolName,
351
+ argsFile: argsPath,
352
+ status: "success",
353
+ command,
354
+ request,
355
+ response
356
+ });
357
+ console.error(pc.green(`Saved run: ${dir}`));
358
+ }
359
+ } catch (error) {
360
+ if (options.saveRun) {
361
+ const dir = await saveRun("runs", {
362
+ id: runId,
363
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
364
+ profile: profileName,
365
+ tool: toolName,
366
+ argsFile: argsPath,
367
+ status: "error",
368
+ command,
369
+ request,
370
+ error: error instanceof Error ? error.message : String(error)
371
+ });
372
+ console.error(pc.yellow(`Saved failed run: ${dir}`));
373
+ }
374
+ throw error;
375
+ }
376
+ });
377
+ cli.command("retry <runDir>", "Retry a saved run, re-reading the args file").action(async (runDir) => {
378
+ const run = await loadRun(runDir);
379
+ const toolArgs = await readJson(run.argsFile);
380
+ const profile = await loadProfile(run.profile);
381
+ const response = await withClient(profile, (client) => client.callTool(run.tool, toolArgs));
382
+ console.log(JSON.stringify(response, null, 2));
383
+ });
384
+ cli.command("summarize <profile>", "Summarize all tools into compact JSON").action(async (profileName) => {
385
+ const profile = await loadProfile(profileName);
386
+ const result = await withClient(profile, (client) => client.listTools());
387
+ console.log(JSON.stringify(summarizeTools(result.tools), null, 2));
388
+ });
389
+ cli.command("benchmark <profile>", "Estimate schema savings for a profile").action(async (profileName) => {
390
+ const profile = await loadProfile(profileName);
391
+ const result = await withClient(profile, (client) => client.listTools());
392
+ const nativeTokens = roughTokens(result);
393
+ const brief = summarizeTools(result.tools);
394
+ const briefTokens = roughTokens(brief);
395
+ console.log(
396
+ JSON.stringify(
397
+ {
398
+ profile: profileName,
399
+ tools: result.tools.length,
400
+ nativeToolsRoughTokens: nativeTokens,
401
+ summarizedToolsRoughTokens: briefTokens,
402
+ estimatedReductionPct: Number(percentReduction(nativeTokens, briefTokens).toFixed(2))
403
+ },
404
+ null,
405
+ 2
406
+ )
407
+ );
408
+ });
409
+ cli.command("render-readme", "Print website hero copy snippets").action(async () => {
410
+ console.log(await readFile3(new URL("../docs/hero-copy.md", import.meta.url), "utf8"));
411
+ });
412
+ cli.help();
413
+ cli.version("0.1.0-alpha.0");
414
+ try {
415
+ cli.parse();
416
+ } catch (error) {
417
+ console.error(pc.red(error instanceof Error ? error.message : String(error)));
418
+ process.exit(1);
419
+ }
420
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/mcp/client.ts","../src/profile.ts","../src/utils/fs.ts","../src/schema/brief.ts","../src/schema/summarize.ts","../src/skill/generator.ts","../src/runs/recorder.ts","../src/utils/tokens.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { cac } from \"cac\";\nimport pc from \"picocolors\";\nimport type { ProfileConfig } from \"./types.js\";\nimport { McpClient } from \"./mcp/client.js\";\nimport { loadProfile } from \"./profile.js\";\nimport { briefTools } from \"./schema/brief.js\";\nimport { summarizeTool, summarizeTools } from \"./schema/summarize.js\";\nimport { generateSkill, writeProfile } from \"./skill/generator.js\";\nimport { createRunId, loadRun, saveRun } from \"./runs/recorder.js\";\nimport { readJson } from \"./utils/fs.js\";\nimport { percentReduction, roughTokens } from \"./utils/tokens.js\";\n\nconst cli = cac(\"toolcapsule\");\n\nasync function withClient<T>(profile: ProfileConfig, fn: (client: McpClient) => Promise<T>): Promise<T> {\n const client = new McpClient(profile);\n try {\n await client.init();\n return await fn(client);\n } finally {\n await client.close();\n }\n}\n\nfunction readArgsPath(raw: string): string {\n return raw.startsWith(\"@\") ? raw.slice(1) : raw;\n}\n\ncli\n .command(\"init <name>\", \"Create a profile and generated Agent Skill for an MCP server\")\n .option(\"--url <url>\", \"Remote MCP URL\")\n .option(\"--command <command>\", \"stdio MCP command\")\n .option(\"--arg <arg>\", \"stdio MCP argument, repeatable\", { type: [String] })\n .option(\"--output <dir>\", \"Skill output directory\")\n .action(async (name: string, options: { url?: string; command?: string; arg?: string[]; output?: string }) => {\n if (!options.url && !options.command) throw new Error(\"Provide --url for remote MCP or --command for stdio MCP\");\n const profile: ProfileConfig = options.url\n ? { name, transport: { type: \"remote\", url: options.url } }\n : { name, transport: { type: \"stdio\", command: options.command!, args: options.arg ?? [] } };\n await writeProfile(join(\".toolcapsule\", \"profiles\", `${name}.json`), profile);\n const out = await generateSkill(profile, options.output ? { outputDir: options.output } : {});\n console.log(pc.green(`Created profile and skill at ${out}`));\n });\n\ncli\n .command(\"tools <profile>\", \"List MCP tools\")\n .option(\"--brief\", \"Print compact tool summaries\")\n .option(\"--names\", \"Print tool names only\")\n .option(\"--json\", \"Print raw JSON\")\n .action(async (profileName: string, options: { brief?: boolean; names?: boolean; json?: boolean }) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n if (options.json) console.log(JSON.stringify(result, null, 2));\n else if (options.names) console.log(result.tools.map((tool) => tool.name).join(\"\\n\"));\n else console.log(briefTools(result.tools));\n });\n\ncli\n .command(\"describe <profile> <tool>\", \"Describe one MCP tool\")\n .option(\"--brief\", \"Print summarized schema instead of raw schema\")\n .action(async (profileName: string, toolName: string, options: { brief?: boolean }) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n const tool = result.tools.find((item) => item.name === toolName);\n if (!tool) throw new Error(`Tool not found: ${toolName}`);\n console.log(JSON.stringify(options.brief ? summarizeTool(tool) : tool, null, 2));\n });\n\ncli\n .command(\"call <profile> <tool> <args>\", \"Call an MCP tool with JSON args or @args.json\")\n .option(\"--save-run\", \"Save request, response, and command under runs/\")\n .action(async (profileName: string, toolName: string, argsRaw: string, options: { saveRun?: boolean }) => {\n const argsPath = readArgsPath(argsRaw);\n const toolArgs = argsRaw.startsWith(\"@\") ? await readJson(argsPath) : JSON.parse(argsRaw);\n const profile = await loadProfile(profileName);\n const request = { name: toolName, arguments: toolArgs };\n const command = `toolcapsule call ${profileName} ${toolName} ${argsRaw}`;\n const runId = createRunId();\n try {\n const response = await withClient(profile, (client) => client.callTool(toolName, toolArgs));\n console.log(JSON.stringify(response, null, 2));\n if (options.saveRun) {\n const dir = await saveRun(\"runs\", {\n id: runId,\n createdAt: new Date().toISOString(),\n profile: profileName,\n tool: toolName,\n argsFile: argsPath,\n status: \"success\",\n command,\n request,\n response,\n });\n console.error(pc.green(`Saved run: ${dir}`));\n }\n } catch (error) {\n if (options.saveRun) {\n const dir = await saveRun(\"runs\", {\n id: runId,\n createdAt: new Date().toISOString(),\n profile: profileName,\n tool: toolName,\n argsFile: argsPath,\n status: \"error\",\n command,\n request,\n error: error instanceof Error ? error.message : String(error),\n });\n console.error(pc.yellow(`Saved failed run: ${dir}`));\n }\n throw error;\n }\n });\n\ncli.command(\"retry <runDir>\", \"Retry a saved run, re-reading the args file\").action(async (runDir: string) => {\n const run = await loadRun(runDir);\n const toolArgs = await readJson(run.argsFile);\n const profile = await loadProfile(run.profile);\n const response = await withClient(profile, (client) => client.callTool(run.tool, toolArgs));\n console.log(JSON.stringify(response, null, 2));\n});\n\ncli.command(\"summarize <profile>\", \"Summarize all tools into compact JSON\").action(async (profileName: string) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n console.log(JSON.stringify(summarizeTools(result.tools), null, 2));\n});\n\ncli.command(\"benchmark <profile>\", \"Estimate schema savings for a profile\").action(async (profileName: string) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n const nativeTokens = roughTokens(result);\n const brief = summarizeTools(result.tools);\n const briefTokens = roughTokens(brief);\n console.log(\n JSON.stringify(\n {\n profile: profileName,\n tools: result.tools.length,\n nativeToolsRoughTokens: nativeTokens,\n summarizedToolsRoughTokens: briefTokens,\n estimatedReductionPct: Number(percentReduction(nativeTokens, briefTokens).toFixed(2)),\n },\n null,\n 2,\n ),\n );\n});\n\ncli.command(\"render-readme\", \"Print website hero copy snippets\").action(async () => {\n console.log(await readFile(new URL(\"../docs/hero-copy.md\", import.meta.url), \"utf8\"));\n});\n\ncli.help();\ncli.version(\"0.1.0-alpha.0\");\n\ntry {\n cli.parse();\n} catch (error) {\n console.error(pc.red(error instanceof Error ? error.message : String(error)));\n process.exit(1);\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { once } from \"node:events\";\nimport type { JsonRpcMessage, ProfileConfig, ToolsListResult } from \"../types.js\";\n\nexport type McpClientOptions = {\n timeoutMs?: number;\n};\n\nexport class McpClient {\n private child: ChildProcessWithoutNullStreams;\n private nextId = 1;\n private buffer = \"\";\n private pending = new Map<\n number,\n { resolve: (value: JsonRpcMessage) => void; reject: (error: Error) => void }\n >();\n private timeoutMs: number;\n\n constructor(profile: ProfileConfig, opts: McpClientOptions = {}) {\n this.timeoutMs = opts.timeoutMs ?? Number(process.env.TOOLCAPSULE_TIMEOUT_MS || \"45000\");\n if (profile.transport.type === \"remote\") {\n this.child = spawn(\"npx\", [\"-y\", \"mcp-remote\", profile.transport.url], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n } else {\n this.child = spawn(profile.transport.command, profile.transport.args ?? [], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n }\n this.child.stdout.setEncoding(\"utf8\");\n this.child.stdout.on(\"data\", (chunk: string) => this.onStdout(chunk));\n this.child.stderr.on(\"data\", (chunk: Buffer) => process.stderr.write(chunk));\n this.child.on(\"exit\", (code, signal) => {\n const error = new Error(`MCP process exited early (code=${code}, signal=${signal})`);\n for (const waiter of this.pending.values()) waiter.reject(error);\n this.pending.clear();\n });\n }\n\n private onStdout(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex: number;\n while ((newlineIndex = this.buffer.indexOf(\"\\n\")) >= 0) {\n const line = this.buffer.slice(0, newlineIndex).trim();\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (!line) continue;\n let message: JsonRpcMessage;\n try {\n message = JSON.parse(line) as JsonRpcMessage;\n } catch {\n process.stderr.write(`${line}\\n`);\n continue;\n }\n if (typeof message.id === \"number\") {\n const waiter = this.pending.get(message.id);\n if (waiter) {\n this.pending.delete(message.id);\n waiter.resolve(message);\n }\n }\n }\n }\n\n async init(): Promise<void> {\n await this.request(\"initialize\", {\n protocolVersion: \"2025-03-26\",\n capabilities: {},\n clientInfo: { name: \"toolcapsule\", version: \"0.1.0\" },\n });\n this.notify(\"notifications/initialized\", {});\n }\n\n async request(method: string, params?: unknown): Promise<unknown> {\n const id = this.nextId++;\n const responsePromise = new Promise<JsonRpcMessage>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n });\n this.child.stdin.write(`${JSON.stringify({ jsonrpc: \"2.0\", id, method, params })}\\n`);\n const response = await this.withTimeout(responsePromise, method);\n if (response.error) throw new Error(`${method} failed: ${JSON.stringify(response.error, null, 2)}`);\n return response.result;\n }\n\n notify(method: string, params?: unknown): void {\n this.child.stdin.write(`${JSON.stringify({ jsonrpc: \"2.0\", method, params })}\\n`);\n }\n\n async listTools(): Promise<ToolsListResult> {\n return (await this.request(\"tools/list\", {})) as ToolsListResult;\n }\n\n async callTool(name: string, args: unknown): Promise<unknown> {\n return await this.request(\"tools/call\", { name, arguments: args });\n }\n\n async close(): Promise<void> {\n if (!this.child.killed) this.child.kill();\n if (this.child.exitCode === null) await Promise.race([once(this.child, \"exit\"), new Promise((resolve) => setTimeout(resolve, 500))]);\n }\n\n private async withTimeout<T>(promise: Promise<T>, label: string): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`${label} timed out after ${this.timeoutMs}ms`)), this.timeoutMs);\n });\n try {\n return await Promise.race([promise, timeout]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport type { ProfileConfig } from \"./types.js\";\nimport { readJson } from \"./utils/fs.js\";\n\nconst profileSchema = z.object({\n name: z.string().min(1),\n transport: z.union([\n z.object({ type: z.literal(\"remote\"), url: z.string().url() }),\n z.object({ type: z.literal(\"stdio\"), command: z.string().min(1), args: z.array(z.string()).optional() }),\n ]),\n skill: z\n .object({\n name: z.string().optional(),\n description: z.string().optional(),\n })\n .optional(),\n shortcuts: z\n .record(\n z.object({\n tool: z.string(),\n description: z.string().optional(),\n args: z.record(z.enum([\"string\", \"file\", \"json\", \"boolean\", \"number\"])).optional(),\n }),\n )\n .optional(),\n});\n\nexport async function loadProfile(profilePathOrName: string): Promise<ProfileConfig> {\n const candidates = [\n profilePathOrName,\n `${profilePathOrName}.json`,\n join(\".toolcapsule\", \"profiles\", `${profilePathOrName}.json`),\n join(\".github\", \"skills\", `${profilePathOrName}-mcp`, \"toolcapsule.config.json\"),\n ];\n const found = candidates.find((path) => existsSync(path));\n if (!found) throw new Error(`Profile not found: ${profilePathOrName}`);\n return profileSchema.parse(await readJson(found)) as ProfileConfig;\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\n\nexport async function readJson<T>(path: string): Promise<T> {\n return JSON.parse(await readFile(path, \"utf8\")) as T;\n}\n\nexport async function writeJson(path: string, value: unknown): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport async function writeText(path: string, value: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, value);\n}\n\nexport function abs(path: string): string {\n return resolve(process.cwd(), path);\n}\n","import type { McpTool } from \"../types.js\";\n\nexport function toolTitle(tool: McpTool): string {\n return tool.annotations?.title || tool.description?.split(/\\n/)[0]?.slice(0, 100) || tool.name;\n}\n\nexport function briefTool(tool: McpTool): string {\n const schema = tool.inputSchema as any;\n const required = Array.isArray(schema?.required) ? schema.required : [];\n const properties = schema?.properties && typeof schema.properties === \"object\" ? Object.keys(schema.properties) : [];\n const flags = [\n tool.annotations?.readOnlyHint ? \"read-only\" : undefined,\n tool.annotations?.destructiveHint ? \"destructive\" : undefined,\n ].filter(Boolean);\n return `- ${tool.name}: ${toolTitle(tool)}${flags.length ? ` [${flags.join(\", \")}]` : \"\"}${required.length ? ` required=${required.join(\"|\")}` : \"\"}${properties.length ? ` args=${properties.slice(0, 12).join(\"|\")}` : \"\"}`;\n}\n\nexport function briefTools(tools: McpTool[]): string {\n return tools.map(briefTool).join(\"\\n\");\n}\n","import type { McpTool } from \"../types.js\";\n\nexport function summarizeTool(tool: McpTool): unknown {\n const schema = tool.inputSchema as any;\n const properties = schema?.properties && typeof schema.properties === \"object\" ? schema.properties : {};\n return {\n name: tool.name,\n title: tool.annotations?.title,\n description: tool.description?.split(/\\n\\n/)[0]?.slice(0, 500),\n annotations: tool.annotations,\n required: Array.isArray(schema?.required) ? schema.required : [],\n arguments: Object.fromEntries(\n Object.entries(properties).map(([key, value]: [string, any]) => [\n key,\n { type: value?.type, enum: value?.enum, description: value?.description?.slice?.(0, 160) },\n ]),\n ),\n };\n}\n\nexport function summarizeTools(tools: McpTool[]): unknown[] {\n return tools.map(summarizeTool);\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport Handlebars from \"handlebars\";\nimport type { ProfileConfig } from \"../types.js\";\nimport { writeJson } from \"../utils/fs.js\";\n\nconst skillTemplate = `---\nname: {{skillName}}\ndescription: '{{description}}'\nargument-hint: 'MCP task, tool name, or args file to run'\n---\n\n# {{title}}\n\nThis skill wraps an MCP server as a lightweight, lazy-loaded, file-first workflow.\n\n## Why use this skill\n\n- Keep full MCP tool schemas out of the model context until needed.\n- Use brief tool summaries for everyday work.\n- Store large payloads in local files such as \\`args.json\\` or \\`content.md\\`.\n- Patch local artifacts and retry failed calls deterministically.\n\n## Commands\n\n\\`\\`\\`bash\ntoolcapsule tools {{profileName}} --brief\ntoolcapsule describe {{profileName}} <tool>\ntoolcapsule call {{profileName}} <tool> @args.json\ntoolcapsule retry <run-dir>\n\\`\\`\\`\n\n## Workflow\n\n1. Use \\`tools --brief\\` to find the likely tool.\n2. Use \\`describe <tool>\\` only when the brief schema is insufficient.\n3. Put complex arguments in a local JSON file.\n4. Call with \\`@args.json\\` and save the run.\n5. On failure, patch the local file and run \\`retry\\`.\n\n## Safety\n\n- Do not fabricate IDs, URLs, user IDs, or opaque tokens.\n- Prefer local files for large payloads.\n- Do not print secrets.\n- Review destructive tools before calling.\n`;\n\nexport type GenerateSkillOptions = {\n outputDir?: string;\n};\n\nexport async function generateSkill(profile: ProfileConfig, opts: GenerateSkillOptions = {}): Promise<string> {\n const skillName = profile.skill?.name || `${profile.name}-mcp`;\n const outputDir = opts.outputDir || join(\".github\", \"skills\", skillName);\n await mkdir(join(outputDir, \"scripts\"), { recursive: true });\n await mkdir(join(outputDir, \"runs\"), { recursive: true });\n\n const template = Handlebars.compile(skillTemplate);\n const description =\n profile.skill?.description ||\n `Use when operating tools from the ${profile.name} MCP server. Lazy-load schemas, call tools through local files, and retry failed calls by patching artifacts.`;\n const markdown = template({\n skillName,\n profileName: profile.name,\n title: `${profile.name} MCP Skill`,\n description: description.replace(/'/g, \"''\"),\n });\n await writeFile(join(outputDir, \"SKILL.md\"), markdown);\n await writeJson(join(outputDir, \"toolcapsule.config.json\"), profile);\n await writeFile(\n join(outputDir, \"scripts\", \"README.md\"),\n `# Scripts\\n\\nThis skill uses the project-level \\`toolcapsule\\` CLI.\\n`,\n );\n return outputDir;\n}\n\nexport async function writeProfile(path: string, profile: ProfileConfig): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeJson(path, profile);\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { RunRecord } from \"../types.js\";\nimport { writeJson } from \"../utils/fs.js\";\n\nexport function createRunId(): string {\n return new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nexport async function saveRun(baseDir: string, record: RunRecord): Promise<string> {\n const dir = join(baseDir, record.id);\n await mkdir(dir, { recursive: true });\n await writeJson(join(dir, \"run.json\"), record);\n await writeJson(join(dir, \"request.json\"), record.request);\n if (record.response !== undefined) await writeJson(join(dir, \"response.json\"), record.response);\n if (record.error) await writeFile(join(dir, \"error.txt\"), record.error);\n await writeFile(join(dir, \"command.txt\"), `${record.command}\\n`);\n return dir;\n}\n\nexport async function loadRun(runDir: string): Promise<RunRecord> {\n return JSON.parse(await readFile(join(runDir, \"run.json\"), \"utf8\")) as RunRecord;\n}\n","export function roughTokens(value: unknown): number {\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n return Math.ceil(text.length / 4);\n}\n\nexport function percentReduction(before: number, after: number): number {\n return before === 0 ? 0 : ((before - after) / before) * 100;\n}\n"],"mappings":";;;AACA,SAAS,YAAAA,iBAAgB;AACzB,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAW;AACpB,OAAO,QAAQ;;;ACJf,SAAS,aAAkD;AAC3D,SAAS,YAAY;AAOd,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU,oBAAI,IAGpB;AAAA,EACM;AAAA,EAER,YAAY,SAAwB,OAAyB,CAAC,GAAG;AAC/D,SAAK,YAAY,KAAK,aAAa,OAAO,QAAQ,IAAI,0BAA0B,OAAO;AACvF,QAAI,QAAQ,UAAU,SAAS,UAAU;AACvC,WAAK,QAAQ,MAAM,OAAO,CAAC,MAAM,cAAc,QAAQ,UAAU,GAAG,GAAG;AAAA,QACrE,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAChC,CAAC;AAAA,IACH,OAAO;AACL,WAAK,QAAQ,MAAM,QAAQ,UAAU,SAAS,QAAQ,UAAU,QAAQ,CAAC,GAAG;AAAA,QAC1E,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAChC,CAAC;AAAA,IACH;AACA,SAAK,MAAM,OAAO,YAAY,MAAM;AACpC,SAAK,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB,KAAK,SAAS,KAAK,CAAC;AACpE,SAAK,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB,QAAQ,OAAO,MAAM,KAAK,CAAC;AAC3E,SAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AACtC,YAAM,QAAQ,IAAI,MAAM,kCAAkC,IAAI,YAAY,MAAM,GAAG;AACnF,iBAAW,UAAU,KAAK,QAAQ,OAAO,EAAG,QAAO,OAAO,KAAK;AAC/D,WAAK,QAAQ,MAAM;AAAA,IACrB,CAAC;AAAA,EACH;AAAA,EAEQ,SAAS,OAAqB;AACpC,SAAK,UAAU;AACf,QAAI;AACJ,YAAQ,eAAe,KAAK,OAAO,QAAQ,IAAI,MAAM,GAAG;AACtD,YAAM,OAAO,KAAK,OAAO,MAAM,GAAG,YAAY,EAAE,KAAK;AACrD,WAAK,SAAS,KAAK,OAAO,MAAM,eAAe,CAAC;AAChD,UAAI,CAAC,KAAM;AACX,UAAI;AACJ,UAAI;AACF,kBAAU,KAAK,MAAM,IAAI;AAAA,MAC3B,QAAQ;AACN,gBAAQ,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAChC;AAAA,MACF;AACA,UAAI,OAAO,QAAQ,OAAO,UAAU;AAClC,cAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ,EAAE;AAC1C,YAAI,QAAQ;AACV,eAAK,QAAQ,OAAO,QAAQ,EAAE;AAC9B,iBAAO,QAAQ,OAAO;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,KAAK,QAAQ,cAAc;AAAA,MAC/B,iBAAiB;AAAA,MACjB,cAAc,CAAC;AAAA,MACf,YAAY,EAAE,MAAM,eAAe,SAAS,QAAQ;AAAA,IACtD,CAAC;AACD,SAAK,OAAO,6BAA6B,CAAC,CAAC;AAAA,EAC7C;AAAA,EAEA,MAAM,QAAQ,QAAgB,QAAoC;AAChE,UAAM,KAAK,KAAK;AAChB,UAAM,kBAAkB,IAAI,QAAwB,CAACC,UAAS,WAAW;AACvE,WAAK,QAAQ,IAAI,IAAI,EAAE,SAAAA,UAAS,OAAO,CAAC;AAAA,IAC1C,CAAC;AACD,SAAK,MAAM,MAAM,MAAM,GAAG,KAAK,UAAU,EAAE,SAAS,OAAO,IAAI,QAAQ,OAAO,CAAC,CAAC;AAAA,CAAI;AACpF,UAAM,WAAW,MAAM,KAAK,YAAY,iBAAiB,MAAM;AAC/D,QAAI,SAAS,MAAO,OAAM,IAAI,MAAM,GAAG,MAAM,YAAY,KAAK,UAAU,SAAS,OAAO,MAAM,CAAC,CAAC,EAAE;AAClG,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,OAAO,QAAgB,QAAwB;AAC7C,SAAK,MAAM,MAAM,MAAM,GAAG,KAAK,UAAU,EAAE,SAAS,OAAO,QAAQ,OAAO,CAAC,CAAC;AAAA,CAAI;AAAA,EAClF;AAAA,EAEA,MAAM,YAAsC;AAC1C,WAAQ,MAAM,KAAK,QAAQ,cAAc,CAAC,CAAC;AAAA,EAC7C;AAAA,EAEA,MAAM,SAAS,MAAc,MAAiC;AAC5D,WAAO,MAAM,KAAK,QAAQ,cAAc,EAAE,MAAM,WAAW,KAAK,CAAC;AAAA,EACnE;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,MAAM,OAAQ,MAAK,MAAM,KAAK;AACxC,QAAI,KAAK,MAAM,aAAa,KAAM,OAAM,QAAQ,KAAK,CAAC,KAAK,KAAK,OAAO,MAAM,GAAG,IAAI,QAAQ,CAACA,aAAY,WAAWA,UAAS,GAAG,CAAC,CAAC,CAAC;AAAA,EACrI;AAAA,EAEA,MAAc,YAAe,SAAqB,OAA2B;AAC3E,QAAI;AACJ,UAAM,UAAU,IAAI,QAAe,CAAC,GAAG,WAAW;AAChD,cAAQ,WAAW,MAAM,OAAO,IAAI,MAAM,GAAG,KAAK,oBAAoB,KAAK,SAAS,IAAI,CAAC,GAAG,KAAK,SAAS;AAAA,IAC5G,CAAC;AACD,QAAI;AACF,aAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,OAAO,CAAC;AAAA,IAC9C,UAAE;AACA,UAAI,MAAO,cAAa,KAAK;AAAA,IAC/B;AAAA,EACF;AACF;;;AC/GA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,SAAS;;;ACFlB,SAAS,OAAO,UAAU,iBAAiB;AAC3C,SAAS,SAAS,eAAe;AAEjC,eAAsB,SAAY,MAA0B;AAC1D,SAAO,KAAK,MAAM,MAAM,SAAS,MAAM,MAAM,CAAC;AAChD;AAEA,eAAsB,UAAU,MAAc,OAA+B;AAC3E,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,UAAU,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,CAAI;AAC7D;;;ADJA,IAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,WAAW,EAAE,MAAM;AAAA,IACjB,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,QAAQ,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAAA,IAC7D,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;AAAA,EACzG,CAAC;AAAA,EACD,OAAO,EACJ,OAAO;AAAA,IACN,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,CAAC,EACA,SAAS;AAAA,EACZ,WAAW,EACR;AAAA,IACC,EAAE,OAAO;AAAA,MACP,MAAM,EAAE,OAAO;AAAA,MACf,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,MACjC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,UAAU,QAAQ,QAAQ,WAAW,QAAQ,CAAC,CAAC,EAAE,SAAS;AAAA,IACnF,CAAC;AAAA,EACH,EACC,SAAS;AACd,CAAC;AAED,eAAsB,YAAY,mBAAmD;AACnF,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,GAAG,iBAAiB;AAAA,IACpB,KAAK,gBAAgB,YAAY,GAAG,iBAAiB,OAAO;AAAA,IAC5D,KAAK,WAAW,UAAU,GAAG,iBAAiB,QAAQ,yBAAyB;AAAA,EACjF;AACA,QAAM,QAAQ,WAAW,KAAK,CAAC,SAAS,WAAW,IAAI,CAAC;AACxD,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sBAAsB,iBAAiB,EAAE;AACrE,SAAO,cAAc,MAAM,MAAM,SAAS,KAAK,CAAC;AAClD;;;AErCO,SAAS,UAAU,MAAuB;AAC/C,SAAO,KAAK,aAAa,SAAS,KAAK,aAAa,MAAM,IAAI,EAAE,CAAC,GAAG,MAAM,GAAG,GAAG,KAAK,KAAK;AAC5F;AAEO,SAAS,UAAU,MAAuB;AAC/C,QAAM,SAAS,KAAK;AACpB,QAAM,WAAW,MAAM,QAAQ,QAAQ,QAAQ,IAAI,OAAO,WAAW,CAAC;AACtE,QAAM,aAAa,QAAQ,cAAc,OAAO,OAAO,eAAe,WAAW,OAAO,KAAK,OAAO,UAAU,IAAI,CAAC;AACnH,QAAM,QAAQ;AAAA,IACZ,KAAK,aAAa,eAAe,cAAc;AAAA,IAC/C,KAAK,aAAa,kBAAkB,gBAAgB;AAAA,EACtD,EAAE,OAAO,OAAO;AAChB,SAAO,KAAK,KAAK,IAAI,KAAK,UAAU,IAAI,CAAC,GAAG,MAAM,SAAS,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,GAAG,SAAS,SAAS,aAAa,SAAS,KAAK,GAAG,CAAC,KAAK,EAAE,GAAG,WAAW,SAAS,SAAS,WAAW,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE;AAC7N;AAEO,SAAS,WAAW,OAA0B;AACnD,SAAO,MAAM,IAAI,SAAS,EAAE,KAAK,IAAI;AACvC;;;ACjBO,SAAS,cAAc,MAAwB;AACpD,QAAM,SAAS,KAAK;AACpB,QAAM,aAAa,QAAQ,cAAc,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa,CAAC;AACtG,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,OAAO,KAAK,aAAa;AAAA,IACzB,aAAa,KAAK,aAAa,MAAM,MAAM,EAAE,CAAC,GAAG,MAAM,GAAG,GAAG;AAAA,IAC7D,aAAa,KAAK;AAAA,IAClB,UAAU,MAAM,QAAQ,QAAQ,QAAQ,IAAI,OAAO,WAAW,CAAC;AAAA,IAC/D,WAAW,OAAO;AAAA,MAChB,OAAO,QAAQ,UAAU,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAqB;AAAA,QAC9D;AAAA,QACA,EAAE,MAAM,OAAO,MAAM,MAAM,OAAO,MAAM,aAAa,OAAO,aAAa,QAAQ,GAAG,GAAG,EAAE;AAAA,MAC3F,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEO,SAAS,eAAe,OAA6B;AAC1D,SAAO,MAAM,IAAI,aAAa;AAChC;;;ACtBA,SAAS,SAAAC,QAAO,aAAAC,kBAAiB;AACjC,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,OAAO,gBAAgB;AAIvB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8CtB,eAAsB,cAAc,SAAwB,OAA6B,CAAC,GAAoB;AAC5G,QAAM,YAAY,QAAQ,OAAO,QAAQ,GAAG,QAAQ,IAAI;AACxD,QAAM,YAAY,KAAK,aAAaC,MAAK,WAAW,UAAU,SAAS;AACvE,QAAMC,OAAMD,MAAK,WAAW,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,QAAMC,OAAMD,MAAK,WAAW,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAExD,QAAM,WAAW,WAAW,QAAQ,aAAa;AACjD,QAAM,cACJ,QAAQ,OAAO,eACf,qCAAqC,QAAQ,IAAI;AACnD,QAAM,WAAW,SAAS;AAAA,IACxB;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,OAAO,GAAG,QAAQ,IAAI;AAAA,IACtB,aAAa,YAAY,QAAQ,MAAM,IAAI;AAAA,EAC7C,CAAC;AACD,QAAME,WAAUF,MAAK,WAAW,UAAU,GAAG,QAAQ;AACrD,QAAM,UAAUA,MAAK,WAAW,yBAAyB,GAAG,OAAO;AACnE,QAAME;AAAA,IACJF,MAAK,WAAW,WAAW,WAAW;AAAA,IACtC;AAAA;AAAA;AAAA;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,aAAa,MAAc,SAAuC;AACtF,QAAMC,OAAME,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,UAAU,MAAM,OAAO;AAC/B;;;AChFA,SAAS,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,QAAAC,aAAY;AAId,SAAS,cAAsB;AACpC,UAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;AACtD;AAEA,eAAsB,QAAQ,SAAiB,QAAoC;AACjF,QAAM,MAAMC,MAAK,SAAS,OAAO,EAAE;AACnC,QAAMC,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAM,UAAUD,MAAK,KAAK,UAAU,GAAG,MAAM;AAC7C,QAAM,UAAUA,MAAK,KAAK,cAAc,GAAG,OAAO,OAAO;AACzD,MAAI,OAAO,aAAa,OAAW,OAAM,UAAUA,MAAK,KAAK,eAAe,GAAG,OAAO,QAAQ;AAC9F,MAAI,OAAO,MAAO,OAAME,WAAUF,MAAK,KAAK,WAAW,GAAG,OAAO,KAAK;AACtE,QAAME,WAAUF,MAAK,KAAK,aAAa,GAAG,GAAG,OAAO,OAAO;AAAA,CAAI;AAC/D,SAAO;AACT;AAEA,eAAsB,QAAQ,QAAoC;AAChE,SAAO,KAAK,MAAM,MAAMG,UAASH,MAAK,QAAQ,UAAU,GAAG,MAAM,CAAC;AACpE;;;ACtBO,SAAS,YAAY,OAAwB;AAClD,QAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AACrE,SAAO,KAAK,KAAK,KAAK,SAAS,CAAC;AAClC;AAEO,SAAS,iBAAiB,QAAgB,OAAuB;AACtE,SAAO,WAAW,IAAI,KAAM,SAAS,SAAS,SAAU;AAC1D;;;ARQA,IAAM,MAAM,IAAI,aAAa;AAE7B,eAAe,WAAc,SAAwB,IAAmD;AACtG,QAAM,SAAS,IAAI,UAAU,OAAO;AACpC,MAAI;AACF,UAAM,OAAO,KAAK;AAClB,WAAO,MAAM,GAAG,MAAM;AAAA,EACxB,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;AAEA,SAAS,aAAa,KAAqB;AACzC,SAAO,IAAI,WAAW,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI;AAC9C;AAEA,IACG,QAAQ,eAAe,8DAA8D,EACrF,OAAO,eAAe,gBAAgB,EACtC,OAAO,uBAAuB,mBAAmB,EACjD,OAAO,eAAe,kCAAkC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EAC1E,OAAO,kBAAkB,wBAAwB,EACjD,OAAO,OAAO,MAAc,YAAiF;AAC5G,MAAI,CAAC,QAAQ,OAAO,CAAC,QAAQ,QAAS,OAAM,IAAI,MAAM,yDAAyD;AAC/G,QAAM,UAAyB,QAAQ,MACnC,EAAE,MAAM,WAAW,EAAE,MAAM,UAAU,KAAK,QAAQ,IAAI,EAAE,IACxD,EAAE,MAAM,WAAW,EAAE,MAAM,SAAS,SAAS,QAAQ,SAAU,MAAM,QAAQ,OAAO,CAAC,EAAE,EAAE;AAC7F,QAAM,aAAaI,MAAK,gBAAgB,YAAY,GAAG,IAAI,OAAO,GAAG,OAAO;AAC5E,QAAM,MAAM,MAAM,cAAc,SAAS,QAAQ,SAAS,EAAE,WAAW,QAAQ,OAAO,IAAI,CAAC,CAAC;AAC5F,UAAQ,IAAI,GAAG,MAAM,gCAAgC,GAAG,EAAE,CAAC;AAC7D,CAAC;AAEH,IACG,QAAQ,mBAAmB,gBAAgB,EAC3C,OAAO,WAAW,8BAA8B,EAChD,OAAO,WAAW,uBAAuB,EACzC,OAAO,UAAU,gBAAgB,EACjC,OAAO,OAAO,aAAqB,YAAkE;AACpG,QAAM,UAAU,MAAM,YAAY,WAAW;AAC7C,QAAM,SAAS,MAAM,WAAW,SAAS,CAAC,WAAW,OAAO,UAAU,CAAC;AACvE,MAAI,QAAQ,KAAM,SAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,WACpD,QAAQ,MAAO,SAAQ,IAAI,OAAO,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,MAC/E,SAAQ,IAAI,WAAW,OAAO,KAAK,CAAC;AAC3C,CAAC;AAEH,IACG,QAAQ,6BAA6B,uBAAuB,EAC5D,OAAO,WAAW,+CAA+C,EACjE,OAAO,OAAO,aAAqB,UAAkB,YAAiC;AACrF,QAAM,UAAU,MAAM,YAAY,WAAW;AAC7C,QAAM,SAAS,MAAM,WAAW,SAAS,CAAC,WAAW,OAAO,UAAU,CAAC;AACvE,QAAM,OAAO,OAAO,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,QAAQ;AAC/D,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,mBAAmB,QAAQ,EAAE;AACxD,UAAQ,IAAI,KAAK,UAAU,QAAQ,QAAQ,cAAc,IAAI,IAAI,MAAM,MAAM,CAAC,CAAC;AACjF,CAAC;AAEH,IACG,QAAQ,gCAAgC,+CAA+C,EACvF,OAAO,cAAc,iDAAiD,EACtE,OAAO,OAAO,aAAqB,UAAkB,SAAiB,YAAmC;AACxG,QAAM,WAAW,aAAa,OAAO;AACrC,QAAM,WAAW,QAAQ,WAAW,GAAG,IAAI,MAAM,SAAS,QAAQ,IAAI,KAAK,MAAM,OAAO;AACxF,QAAM,UAAU,MAAM,YAAY,WAAW;AAC7C,QAAM,UAAU,EAAE,MAAM,UAAU,WAAW,SAAS;AACtD,QAAM,UAAU,oBAAoB,WAAW,IAAI,QAAQ,IAAI,OAAO;AACtE,QAAM,QAAQ,YAAY;AAC1B,MAAI;AACF,UAAM,WAAW,MAAM,WAAW,SAAS,CAAC,WAAW,OAAO,SAAS,UAAU,QAAQ,CAAC;AAC1F,YAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC7C,QAAI,QAAQ,SAAS;AACnB,YAAM,MAAM,MAAM,QAAQ,QAAQ;AAAA,QAChC,IAAI;AAAA,QACJ,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,SAAS;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,cAAQ,MAAM,GAAG,MAAM,cAAc,GAAG,EAAE,CAAC;AAAA,IAC7C;AAAA,EACF,SAAS,OAAO;AACd,QAAI,QAAQ,SAAS;AACnB,YAAM,MAAM,MAAM,QAAQ,QAAQ;AAAA,QAChC,IAAI;AAAA,QACJ,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,SAAS;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AACD,cAAQ,MAAM,GAAG,OAAO,qBAAqB,GAAG,EAAE,CAAC;AAAA,IACrD;AACA,UAAM;AAAA,EACR;AACF,CAAC;AAEH,IAAI,QAAQ,kBAAkB,6CAA6C,EAAE,OAAO,OAAO,WAAmB;AAC5G,QAAM,MAAM,MAAM,QAAQ,MAAM;AAChC,QAAM,WAAW,MAAM,SAAS,IAAI,QAAQ;AAC5C,QAAM,UAAU,MAAM,YAAY,IAAI,OAAO;AAC7C,QAAM,WAAW,MAAM,WAAW,SAAS,CAAC,WAAW,OAAO,SAAS,IAAI,MAAM,QAAQ,CAAC;AAC1F,UAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,IAAI,QAAQ,uBAAuB,uCAAuC,EAAE,OAAO,OAAO,gBAAwB;AAChH,QAAM,UAAU,MAAM,YAAY,WAAW;AAC7C,QAAM,SAAS,MAAM,WAAW,SAAS,CAAC,WAAW,OAAO,UAAU,CAAC;AACvE,UAAQ,IAAI,KAAK,UAAU,eAAe,OAAO,KAAK,GAAG,MAAM,CAAC,CAAC;AACnE,CAAC;AAED,IAAI,QAAQ,uBAAuB,uCAAuC,EAAE,OAAO,OAAO,gBAAwB;AAChH,QAAM,UAAU,MAAM,YAAY,WAAW;AAC7C,QAAM,SAAS,MAAM,WAAW,SAAS,CAAC,WAAW,OAAO,UAAU,CAAC;AACvE,QAAM,eAAe,YAAY,MAAM;AACvC,QAAM,QAAQ,eAAe,OAAO,KAAK;AACzC,QAAM,cAAc,YAAY,KAAK;AACrC,UAAQ;AAAA,IACN,KAAK;AAAA,MACH;AAAA,QACE,SAAS;AAAA,QACT,OAAO,OAAO,MAAM;AAAA,QACpB,wBAAwB;AAAA,QACxB,4BAA4B;AAAA,QAC5B,uBAAuB,OAAO,iBAAiB,cAAc,WAAW,EAAE,QAAQ,CAAC,CAAC;AAAA,MACtF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAED,IAAI,QAAQ,iBAAiB,kCAAkC,EAAE,OAAO,YAAY;AAClF,UAAQ,IAAI,MAAMC,UAAS,IAAI,IAAI,wBAAwB,YAAY,GAAG,GAAG,MAAM,CAAC;AACtF,CAAC;AAED,IAAI,KAAK;AACT,IAAI,QAAQ,eAAe;AAE3B,IAAI;AACF,MAAI,MAAM;AACZ,SAAS,OAAO;AACd,UAAQ,MAAM,GAAG,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,CAAC;AAC5E,UAAQ,KAAK,CAAC;AAChB;","names":["readFile","join","resolve","mkdir","writeFile","dirname","join","join","mkdir","writeFile","dirname","mkdir","readFile","writeFile","join","join","mkdir","writeFile","readFile","join","readFile"]}
@@ -0,0 +1,18 @@
1
+ # Architecture
2
+
3
+ ```mermaid
4
+ flowchart TD
5
+ A[Remote or stdio MCP server] --> B[toolcapsule CLI]
6
+ B --> C[Tool cache and brief schema]
7
+ B --> D[Generated SKILL.md]
8
+ B --> E[Run artifacts]
9
+ E --> F[Patch and retry]
10
+ ```
11
+
12
+ ## Components
13
+
14
+ - `McpClient`: speaks JSON-RPC over stdio, using `mcp-remote` for remote endpoints.
15
+ - `Profile`: stores transport and shortcut configuration.
16
+ - `Skill generator`: writes `SKILL.md` and profile config.
17
+ - `Run recorder`: stores request, response, command, and error files.
18
+ - `Schema helpers`: produce compact tool summaries.
@@ -0,0 +1,25 @@
1
+ # Benchmark methodology
2
+
3
+ A useful benchmark compares two modes with the same model, same prompts, and same scenarios.
4
+
5
+ ## Native MCP mode
6
+
7
+ Every model call includes the complete MCP tool definitions.
8
+
9
+ ## Skill mode
10
+
11
+ Every model call includes only compact Skill instructions and shortcut schemas.
12
+
13
+ ## Metrics
14
+
15
+ - input tokens per turn;
16
+ - total input tokens;
17
+ - output tokens;
18
+ - latency;
19
+ - success rate;
20
+ - repeated schema reads;
21
+ - retry count.
22
+
23
+ ## Interpretation
24
+
25
+ The benchmark estimates context savings. It does not automatically prove real tool-call success unless live tool execution is enabled.
@@ -0,0 +1,19 @@
1
+ # Concept
2
+
3
+ **ToolCapsule** is a lazy workflow layer for heavy MCP tools.
4
+
5
+ MCP connects agents to tools. Skills package repeatable agent workflows. ToolCapsule sits between them: it keeps MCP as the source of truth, but turns schema-heavy tools into compact, file-first Agent Skills.
6
+
7
+ ## The idea
8
+
9
+ ```text
10
+ MCP tool schema → compact Skill guidance → local capsule files → auditable run → patch and retry
11
+ ```
12
+
13
+ ## Principles
14
+
15
+ 1. Brief by default, full MCP schema on demand.
16
+ 2. Large tool payloads live in local files.
17
+ 3. Every call can be recorded as a ToolCapsule run.
18
+ 4. Failed calls should be patchable and retryable.
19
+ 5. MCP remains the capability layer; Skill becomes the agent-facing workflow layer.
@@ -0,0 +1,13 @@
1
+ # Hero copy
2
+
3
+ Heavy MCP tools don't belong in your prompt.
4
+
5
+ Put them in a ToolCapsule.
6
+
7
+ ToolCapsule turns schema-heavy MCP servers into lightweight, lazy-loaded Agent Skills with file-first calls and patch-and-retry recovery.
8
+
9
+ - Save context.
10
+ - Generate Skills.
11
+ - Store big payloads as files.
12
+ - Patch failed calls.
13
+ - Retry deterministically.
@@ -0,0 +1,21 @@
1
+ # Patch and retry
2
+
3
+ Native MCP failures often require the model to regenerate a full tool call. For large payloads, that is expensive and error-prone.
4
+
5
+ `toolcapsule` recommends file-first calls:
6
+
7
+ ```bash
8
+ toolcapsule call feishu create-doc @args.json --save-run
9
+ ```
10
+
11
+ If the call fails:
12
+
13
+ 1. inspect `runs/<id>/error.txt`;
14
+ 2. patch `args.json` or the referenced Markdown file;
15
+ 3. retry:
16
+
17
+ ```bash
18
+ toolcapsule retry runs/<id>
19
+ ```
20
+
21
+ This makes failures reproducible and local.
@@ -0,0 +1,15 @@
1
+ # Release checklist
2
+
3
+ Before publishing v0.1.0:
4
+
5
+ - [ ] Create the GitHub repository.
6
+ - [ ] Replace any remaining placeholder links.
7
+ - [ ] Confirm the npm package name `toolcapsule` is available, or choose a scoped package.
8
+ - [ ] Connect the repository to Vercel.
9
+ - [ ] Point `toolcapsule.studio` to Vercel.
10
+ - [ ] Enable GitHub Discussions.
11
+ - [ ] Add `NPM_TOKEN` or configure npm trusted publishing.
12
+ - [ ] Review the security model for local command execution.
13
+ - [ ] Run `pnpm run ci`.
14
+ - [ ] Run CLI smoke tests with a mock MCP server.
15
+ - [ ] Tag `v0.1.0-alpha.0` or publish manually with `pnpm publish --access public`.
@@ -0,0 +1,9 @@
1
+ # Feishu example
2
+
3
+ ```bash
4
+ toolcapsule init feishu --url https://mcp.feishu.cn/mcp/your-endpoint
5
+ tcap tools feishu --brief
6
+ tcap call feishu create-doc @examples/feishu/args.create-doc.json --save-run
7
+ ```
8
+
9
+ Use a test folder or wiki space for live writes.
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "ToolCapsule test document",
3
+ "markdown": "## Hello\n\nThis document was generated from a local args file."
4
+ }
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "toolcapsule",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "File-first, patchable Agent Skills for heavy MCP tools.",
5
+ "type": "module",
6
+ "bin": {
7
+ "toolcapsule": "./dist/cli.js",
8
+ "tcap": "./dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE",
14
+ "docs",
15
+ "examples"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "dev": "tsx src/cli.ts",
20
+ "typecheck": "tsc --noEmit",
21
+ "test": "vitest run",
22
+ "lint": "eslint .",
23
+ "format": "prettier --write .",
24
+ "format:check": "prettier --check .",
25
+ "ci": "tsc --noEmit && eslint . && vitest run && tsup",
26
+ "smoke": "node dist/cli.js --help"
27
+ },
28
+ "keywords": [
29
+ "mcp",
30
+ "model-context-protocol",
31
+ "agent-skills",
32
+ "skills",
33
+ "skill",
34
+ "tool-calling",
35
+ "function-calling",
36
+ "ai-agents",
37
+ "llm-tools",
38
+ "context-engineering",
39
+ "cli",
40
+ "typescript"
41
+ ],
42
+ "author": "",
43
+ "license": "MIT",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/RainSunMe/toolcapsule.git"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/RainSunMe/toolcapsule/issues"
50
+ },
51
+ "homepage": "https://toolcapsule.studio",
52
+ "engines": {
53
+ "node": ">=20"
54
+ },
55
+ "packageManager": "pnpm@9.15.9",
56
+ "dependencies": {
57
+ "cac": "^6.7.14",
58
+ "execa": "^9.6.0",
59
+ "handlebars": "^4.7.8",
60
+ "picocolors": "^1.1.1",
61
+ "zod": "^3.24.1"
62
+ },
63
+ "devDependencies": {
64
+ "@eslint/js": "^9.18.0",
65
+ "@types/node": "^22.10.7",
66
+ "eslint": "^9.18.0",
67
+ "prettier": "^3.4.2",
68
+ "tsup": "^8.3.5",
69
+ "tsx": "^4.19.2",
70
+ "typescript": "^5.7.3",
71
+ "typescript-eslint": "^8.20.0",
72
+ "vercel": "^54.9.1",
73
+ "vitest": "^2.1.8"
74
+ }
75
+ }