@julong/mono-rele2-utils 1.3.1 → 1.5.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/README.md +18 -15
- package/dist/cli.js +28 -13
- package/dist/index.d.ts +80 -1
- package/dist/index.js +221 -17
- package/dist/server.js +40 -20
- package/dist/skills/mono-rele2-utils/skill.md +56 -0
- package/package.json +2 -1
- package/dist/mono-rele2-utils/skill.md +0 -58
package/README.md
CHANGED
|
@@ -36,40 +36,41 @@ Available skills:
|
|
|
36
36
|
|
|
37
37
|
### Skills
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
<!-- SKILLS:START -->
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
#### `cnTool`
|
|
42
|
+
|
|
43
|
+
Merges class names, filtering out falsy values.
|
|
42
44
|
|
|
43
45
|
```sh
|
|
44
|
-
mono-rele2-utils
|
|
46
|
+
mono-rele2-utils cnTool <classes>
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
| arg | type | description |
|
|
48
50
|
|-----|------|-------------|
|
|
49
|
-
| `
|
|
50
|
-
| `to` | `upper` \| `lower` \| `capitalize` \| `camel` \| `snake` \| `kebab` | Target case format |
|
|
51
|
+
| `classes` | JSON string (array) | List of class names to merge |
|
|
51
52
|
|
|
52
53
|
```sh
|
|
53
|
-
mono-rele2-utils
|
|
54
|
-
mono-rele2-utils caseConvertTool "helloWorld" snake # hello_world
|
|
55
|
-
mono-rele2-utils caseConvertTool "hello world" kebab # hello-world
|
|
56
|
-
mono-rele2-utils caseConvertTool "hello world" upper # HELLO WORLD
|
|
54
|
+
mono-rele2-utils cnTool '["btn","active","large"]' # btn active large
|
|
57
55
|
```
|
|
58
56
|
|
|
59
|
-
#### `
|
|
57
|
+
#### `caseConvertTool`
|
|
60
58
|
|
|
61
|
-
|
|
59
|
+
Converts text to the specified case format.
|
|
62
60
|
|
|
63
61
|
```sh
|
|
64
|
-
mono-rele2-utils
|
|
62
|
+
mono-rele2-utils caseConvertTool <input> <to>
|
|
65
63
|
```
|
|
66
64
|
|
|
67
65
|
| arg | type | description |
|
|
68
66
|
|-----|------|-------------|
|
|
69
|
-
| `
|
|
67
|
+
| `input` | string | Text to convert |
|
|
68
|
+
| `to` | `upper` \| `lower` \| `capitalize` \| `camel` \| `snake` \| `kebab` | Target case format |
|
|
70
69
|
|
|
71
70
|
```sh
|
|
72
|
-
mono-rele2-utils
|
|
71
|
+
mono-rele2-utils caseConvertTool "hello world" camel # helloWorld
|
|
72
|
+
mono-rele2-utils caseConvertTool "helloWorld" snake # hello_world
|
|
73
|
+
mono-rele2-utils caseConvertTool "hello world" kebab # hello-world
|
|
73
74
|
```
|
|
74
75
|
|
|
75
76
|
#### `truncateTool`
|
|
@@ -84,13 +85,15 @@ mono-rele2-utils truncateTool <input> <maxLength> [suffix]
|
|
|
84
85
|
|-----|------|-------------|
|
|
85
86
|
| `input` | string | Text to truncate |
|
|
86
87
|
| `maxLength` | number | Maximum character length |
|
|
87
|
-
| `suffix` | string | Suffix to append (default: `...`) |
|
|
88
|
+
| `suffix` | string | Suffix to append when truncated (default: `...`) |
|
|
88
89
|
|
|
89
90
|
```sh
|
|
90
91
|
mono-rele2-utils truncateTool "hello world long text" 10 # hello w...
|
|
91
92
|
mono-rele2-utils truncateTool "hello world" 8 "…" # hello w…
|
|
92
93
|
```
|
|
93
94
|
|
|
95
|
+
<!-- SKILLS:END -->
|
|
96
|
+
|
|
94
97
|
## MCP Server
|
|
95
98
|
|
|
96
99
|
```sh
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../common/
|
|
3
|
+
// ../common/kit/tool.ts
|
|
4
4
|
function toolDef(def) {
|
|
5
5
|
return def;
|
|
6
6
|
}
|
|
@@ -11,11 +11,11 @@ function text(content) {
|
|
|
11
11
|
return { content: [{ type: "text", text: content }] };
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// ../common/
|
|
14
|
+
// ../common/kit/server.ts
|
|
15
15
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
16
16
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
17
|
|
|
18
|
-
// ../common/
|
|
18
|
+
// ../common/kit/cli.ts
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
function formatSkills(tools2) {
|
|
21
21
|
const sections = Object.entries(tools2).map(([key, tool]) => {
|
|
@@ -66,9 +66,12 @@ function handleCliError(err) {
|
|
|
66
66
|
process.exit(1);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// ../common/kit/skill.ts
|
|
70
70
|
import { z as z2 } from "zod";
|
|
71
71
|
|
|
72
|
+
// src/tools/text.ts
|
|
73
|
+
import { z as z3 } from "zod";
|
|
74
|
+
|
|
72
75
|
// src/cn.ts
|
|
73
76
|
function cn(...classes) {
|
|
74
77
|
return classes.filter(Boolean).join(" ");
|
|
@@ -80,31 +83,43 @@ var tools = {
|
|
|
80
83
|
name: "cn",
|
|
81
84
|
description: "Merges class names, filtering out falsy values",
|
|
82
85
|
inputSchema: {
|
|
83
|
-
classes:
|
|
86
|
+
classes: z3.array(z3.string()).describe("List of class names to merge")
|
|
84
87
|
},
|
|
85
|
-
handler: async ({ classes }) => text(cn(...classes))
|
|
88
|
+
handler: async ({ classes }) => text(cn(...classes)),
|
|
89
|
+
examples: [
|
|
90
|
+
{ args: [`'["btn","active","large"]'`], result: "btn active large" }
|
|
91
|
+
]
|
|
86
92
|
}),
|
|
87
93
|
caseConvertTool: toolDef({
|
|
88
94
|
name: "case_convert",
|
|
89
95
|
description: "Converts text to the specified case format",
|
|
90
96
|
inputSchema: {
|
|
91
|
-
input:
|
|
92
|
-
to:
|
|
97
|
+
input: z3.string().describe("Text to convert"),
|
|
98
|
+
to: z3.enum(["upper", "lower", "capitalize", "camel", "snake", "kebab"]).describe("Target case format")
|
|
93
99
|
},
|
|
94
|
-
handler: async ({ input, to }) => text(convert(input, to))
|
|
100
|
+
handler: async ({ input, to }) => text(convert(input, to)),
|
|
101
|
+
examples: [
|
|
102
|
+
{ args: [`"hello world"`, "camel"], result: "helloWorld" },
|
|
103
|
+
{ args: [`"helloWorld"`, "snake"], result: "hello_world" },
|
|
104
|
+
{ args: [`"hello world"`, "kebab"], result: "hello-world" }
|
|
105
|
+
]
|
|
95
106
|
}),
|
|
96
107
|
truncateTool: toolDef({
|
|
97
108
|
name: "truncate",
|
|
98
109
|
description: "Truncates text to a maximum length and appends a suffix",
|
|
99
110
|
inputSchema: {
|
|
100
|
-
input:
|
|
101
|
-
maxLength:
|
|
102
|
-
suffix:
|
|
111
|
+
input: z3.string().describe("Text to truncate"),
|
|
112
|
+
maxLength: z3.number().int().positive().describe("Maximum character length"),
|
|
113
|
+
suffix: z3.string().default("...").describe("Suffix to append when truncated")
|
|
103
114
|
},
|
|
104
115
|
handler: async ({ input, maxLength, suffix }) => {
|
|
105
116
|
const result = input.length <= maxLength ? input : input.slice(0, maxLength - suffix.length) + suffix;
|
|
106
117
|
return text(result);
|
|
107
|
-
}
|
|
118
|
+
},
|
|
119
|
+
examples: [
|
|
120
|
+
{ args: [`"hello world long text"`, "10"], result: "hello w..." },
|
|
121
|
+
{ args: [`"hello world"`, "8", `"\u2026"`], result: "hello w\u2026" }
|
|
122
|
+
]
|
|
108
123
|
})
|
|
109
124
|
};
|
|
110
125
|
var cnTool = defineTool(tools.cnTool);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,86 @@
|
|
|
1
1
|
import * as _modelcontextprotocol_sdk_server_mcp_js from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { z } from 'zod';
|
|
2
4
|
|
|
3
5
|
declare function cn(...classes: (string | undefined | null | false)[]): string;
|
|
4
6
|
|
|
7
|
+
type ToolResult = CallToolResult;
|
|
8
|
+
type ToolExample = {
|
|
9
|
+
args: string[];
|
|
10
|
+
result: string;
|
|
11
|
+
};
|
|
12
|
+
type AnyToolDef = {
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
inputSchema: z.ZodRawShape;
|
|
16
|
+
handler: (args: any) => Promise<ToolResult>;
|
|
17
|
+
examples?: ToolExample[];
|
|
18
|
+
guidelines?: string[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type SkillTools = Record<string, AnyToolDef>;
|
|
22
|
+
declare function generateSkillMarkdown(opts: {
|
|
23
|
+
binName: string;
|
|
24
|
+
description: string;
|
|
25
|
+
tools: SkillTools;
|
|
26
|
+
}): string;
|
|
27
|
+
declare function generateReadmeSkills(opts: {
|
|
28
|
+
binName: string;
|
|
29
|
+
tools: SkillTools;
|
|
30
|
+
}): string;
|
|
31
|
+
|
|
32
|
+
declare const tools: {
|
|
33
|
+
cnTool: {
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
inputSchema: {
|
|
37
|
+
readonly classes: z.ZodArray<z.ZodString>;
|
|
38
|
+
};
|
|
39
|
+
handler: (input: {
|
|
40
|
+
classes: string[];
|
|
41
|
+
}) => Promise<ToolResult>;
|
|
42
|
+
examples?: ToolExample[];
|
|
43
|
+
guidelines?: string[];
|
|
44
|
+
};
|
|
45
|
+
caseConvertTool: {
|
|
46
|
+
name: string;
|
|
47
|
+
description: string;
|
|
48
|
+
inputSchema: {
|
|
49
|
+
readonly input: z.ZodString;
|
|
50
|
+
readonly to: z.ZodEnum<{
|
|
51
|
+
upper: "upper";
|
|
52
|
+
lower: "lower";
|
|
53
|
+
capitalize: "capitalize";
|
|
54
|
+
camel: "camel";
|
|
55
|
+
snake: "snake";
|
|
56
|
+
kebab: "kebab";
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
handler: (input: {
|
|
60
|
+
input: string;
|
|
61
|
+
to: "upper" | "lower" | "capitalize" | "camel" | "snake" | "kebab";
|
|
62
|
+
}) => Promise<ToolResult>;
|
|
63
|
+
examples?: ToolExample[];
|
|
64
|
+
guidelines?: string[];
|
|
65
|
+
};
|
|
66
|
+
truncateTool: {
|
|
67
|
+
name: string;
|
|
68
|
+
description: string;
|
|
69
|
+
inputSchema: {
|
|
70
|
+
readonly input: z.ZodString;
|
|
71
|
+
readonly maxLength: z.ZodNumber;
|
|
72
|
+
readonly suffix: z.ZodDefault<z.ZodString>;
|
|
73
|
+
};
|
|
74
|
+
handler: (input: {
|
|
75
|
+
input: string;
|
|
76
|
+
maxLength: number;
|
|
77
|
+
suffix: string;
|
|
78
|
+
}) => Promise<ToolResult>;
|
|
79
|
+
examples?: ToolExample[];
|
|
80
|
+
guidelines?: string[];
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
5
84
|
declare function createUtilsServer(): _modelcontextprotocol_sdk_server_mcp_js.McpServer;
|
|
6
85
|
|
|
7
|
-
export { cn, createUtilsServer };
|
|
86
|
+
export { cn, createUtilsServer, generateReadmeSkills, generateSkillMarkdown, tools };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../common/
|
|
1
|
+
// ../common/kit/tool.ts
|
|
2
2
|
function toolDef(def) {
|
|
3
3
|
return def;
|
|
4
4
|
}
|
|
@@ -9,27 +9,211 @@ function text(content) {
|
|
|
9
9
|
return { content: [{ type: "text", text: content }] };
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
// ../common/
|
|
12
|
+
// ../common/kit/server.ts
|
|
13
13
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
14
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
15
|
function createMcpServer(config, tools2) {
|
|
16
|
-
const
|
|
16
|
+
const server2 = new McpServer(config);
|
|
17
17
|
for (const tool of tools2) {
|
|
18
|
-
|
|
18
|
+
server2.registerTool(
|
|
19
19
|
tool.name,
|
|
20
20
|
{ description: tool.description, inputSchema: tool.inputSchema },
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
22
|
tool.handler
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
|
-
return
|
|
25
|
+
return server2;
|
|
26
|
+
}
|
|
27
|
+
async function startServer(server2) {
|
|
28
|
+
const transport = new StdioServerTransport();
|
|
29
|
+
await server2.connect(transport);
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
// ../common/
|
|
32
|
+
// ../common/kit/cli.ts
|
|
29
33
|
import { z } from "zod";
|
|
30
34
|
|
|
31
|
-
//
|
|
35
|
+
// ../common/kit/skill.ts
|
|
32
36
|
import { z as z2 } from "zod";
|
|
37
|
+
function generateSkillMarkdown(opts) {
|
|
38
|
+
const { binName, description, tools: tools2 } = opts;
|
|
39
|
+
return `---
|
|
40
|
+
name: ${binName}
|
|
41
|
+
description: ${description}
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# ${binName}
|
|
45
|
+
|
|
46
|
+
\`\`\`sh
|
|
47
|
+
${binName} <skillName> [...args]
|
|
48
|
+
\`\`\`
|
|
49
|
+
|
|
50
|
+
## Skills
|
|
51
|
+
|
|
52
|
+
${renderSkills(tools2)}
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
${renderExamples(binName, tools2)}
|
|
57
|
+
|
|
58
|
+
## Guidelines
|
|
59
|
+
|
|
60
|
+
${renderGuidelines(binName, tools2)}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
function renderSkills(tools2) {
|
|
64
|
+
return Object.entries(tools2).map(([key, tool]) => {
|
|
65
|
+
const rows = Object.entries(tool.inputSchema).map(([field, schema]) => `| \`${field}\` | ${describeField(schema)} |`).join("\n");
|
|
66
|
+
return `### ${key}
|
|
67
|
+
|
|
68
|
+
${tool.description}
|
|
69
|
+
|
|
70
|
+
| arg | description |
|
|
71
|
+
|-----|-------------|
|
|
72
|
+
${rows}`;
|
|
73
|
+
}).join("\n\n");
|
|
74
|
+
}
|
|
75
|
+
function describeField(schema) {
|
|
76
|
+
const baseDesc = schema.description ?? "";
|
|
77
|
+
let inner = schema;
|
|
78
|
+
let defaultValue;
|
|
79
|
+
let isOptional = false;
|
|
80
|
+
while (inner instanceof z2.ZodOptional || inner instanceof z2.ZodDefault) {
|
|
81
|
+
if (inner instanceof z2.ZodDefault) {
|
|
82
|
+
const raw = inner.def.defaultValue;
|
|
83
|
+
defaultValue = typeof raw === "function" ? raw() : raw;
|
|
84
|
+
}
|
|
85
|
+
if (inner instanceof z2.ZodOptional) isOptional = true;
|
|
86
|
+
inner = inner.unwrap();
|
|
87
|
+
}
|
|
88
|
+
const parts = [];
|
|
89
|
+
if (baseDesc) parts.push(baseDesc);
|
|
90
|
+
if (inner instanceof z2.ZodEnum) {
|
|
91
|
+
const values = inner.options.map((v) => `\`${v}\``).join(" \\| ");
|
|
92
|
+
parts.push(values);
|
|
93
|
+
}
|
|
94
|
+
if (defaultValue !== void 0) parts.push(`default: \`${String(defaultValue)}\``);
|
|
95
|
+
else if (isOptional) parts.push("optional");
|
|
96
|
+
return parts.join(" \u2014 ");
|
|
97
|
+
}
|
|
98
|
+
function renderExamples(binName, tools2) {
|
|
99
|
+
const lines = [];
|
|
100
|
+
for (const [key, tool] of Object.entries(tools2)) {
|
|
101
|
+
if (!tool.examples) continue;
|
|
102
|
+
for (const ex of tool.examples) {
|
|
103
|
+
const cmd = [binName, key, ...ex.args].join(" ");
|
|
104
|
+
lines.push(`- \`${cmd}\` => \`${ex.result}\``);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return lines.join("\n");
|
|
108
|
+
}
|
|
109
|
+
function renderGuidelines(binName, tools2) {
|
|
110
|
+
const items = [];
|
|
111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
112
|
+
const push = (g) => {
|
|
113
|
+
if (seen.has(g)) return;
|
|
114
|
+
seen.add(g);
|
|
115
|
+
items.push(g);
|
|
116
|
+
};
|
|
117
|
+
push("Arguments are positional \u2014 pass them in the order listed in each skill's table");
|
|
118
|
+
const allSchemas = Object.values(tools2).flatMap((t) => Object.values(t.inputSchema));
|
|
119
|
+
if (allSchemas.some((s) => containsType(s, z2.ZodNumber))) {
|
|
120
|
+
push("Numeric args are auto-parsed \u2014 pass as plain numbers (e.g. `10`)");
|
|
121
|
+
}
|
|
122
|
+
if (allSchemas.some((s) => containsType(s, z2.ZodArray))) {
|
|
123
|
+
push('Array args must be valid JSON \u2014 wrap in single quotes on Unix shells (e.g. `\'["a","b"]\'`)');
|
|
124
|
+
}
|
|
125
|
+
if (allSchemas.some((s) => s instanceof z2.ZodOptional || s instanceof z2.ZodDefault)) {
|
|
126
|
+
push("Optional args with defaults may be omitted");
|
|
127
|
+
}
|
|
128
|
+
for (const tool of Object.values(tools2)) {
|
|
129
|
+
if (!tool.guidelines) continue;
|
|
130
|
+
for (const g of tool.guidelines) push(g);
|
|
131
|
+
}
|
|
132
|
+
push(`Run \`${binName}\` with no args to list all available skills`);
|
|
133
|
+
return items.map((g) => `- ${g}`).join("\n");
|
|
134
|
+
}
|
|
135
|
+
function containsType(schema, ctor) {
|
|
136
|
+
let inner = schema;
|
|
137
|
+
while (inner instanceof z2.ZodOptional || inner instanceof z2.ZodDefault) {
|
|
138
|
+
inner = inner.unwrap();
|
|
139
|
+
}
|
|
140
|
+
return inner instanceof ctor;
|
|
141
|
+
}
|
|
142
|
+
function generateReadmeSkills(opts) {
|
|
143
|
+
const { binName, tools: tools2 } = opts;
|
|
144
|
+
return Object.entries(tools2).map(([key, tool]) => renderReadmeSkill(binName, key, tool)).join("\n\n");
|
|
145
|
+
}
|
|
146
|
+
function renderReadmeSkill(binName, key, tool) {
|
|
147
|
+
const fields = Object.entries(tool.inputSchema);
|
|
148
|
+
const usageArgs = fields.map(([field, schema]) => {
|
|
149
|
+
const isOpt = schema instanceof z2.ZodOptional || schema instanceof z2.ZodDefault;
|
|
150
|
+
return isOpt ? `[${field}]` : `<${field}>`;
|
|
151
|
+
}).join(" ");
|
|
152
|
+
const usage = [binName, key, usageArgs].filter(Boolean).join(" ");
|
|
153
|
+
const rows = fields.map(([field, schema]) => {
|
|
154
|
+
const t = describeReadmeType(schema);
|
|
155
|
+
const d = describeReadmeDesc(schema);
|
|
156
|
+
return `| \`${field}\` | ${t} | ${d} |`;
|
|
157
|
+
}).join("\n");
|
|
158
|
+
const examples = tool.examples ?? [];
|
|
159
|
+
let exampleBlock = "";
|
|
160
|
+
if (examples.length > 0) {
|
|
161
|
+
const cmds = examples.map((ex) => [binName, key, ...ex.args].join(" "));
|
|
162
|
+
const width = Math.max(...cmds.map((c) => c.length));
|
|
163
|
+
const lines = examples.map((ex, i) => `${cmds[i].padEnd(width)} # ${ex.result}`);
|
|
164
|
+
exampleBlock = `
|
|
165
|
+
|
|
166
|
+
\`\`\`sh
|
|
167
|
+
${lines.join("\n")}
|
|
168
|
+
\`\`\``;
|
|
169
|
+
}
|
|
170
|
+
const tableBlock = rows ? `
|
|
171
|
+
|
|
172
|
+
| arg | type | description |
|
|
173
|
+
|-----|------|-------------|
|
|
174
|
+
${rows}` : "";
|
|
175
|
+
return `#### \`${key}\`
|
|
176
|
+
|
|
177
|
+
${tool.description}.
|
|
178
|
+
|
|
179
|
+
\`\`\`sh
|
|
180
|
+
${usage}
|
|
181
|
+
\`\`\`${tableBlock}${exampleBlock}`;
|
|
182
|
+
}
|
|
183
|
+
function describeReadmeType(schema) {
|
|
184
|
+
let inner = schema;
|
|
185
|
+
while (inner instanceof z2.ZodOptional || inner instanceof z2.ZodDefault) {
|
|
186
|
+
inner = inner.unwrap();
|
|
187
|
+
}
|
|
188
|
+
if (inner instanceof z2.ZodEnum) {
|
|
189
|
+
return inner.options.map((v) => `\`${v}\``).join(" \\| ");
|
|
190
|
+
}
|
|
191
|
+
if (inner instanceof z2.ZodNumber) return "number";
|
|
192
|
+
if (inner instanceof z2.ZodString) return "string";
|
|
193
|
+
if (inner instanceof z2.ZodBoolean) return "boolean";
|
|
194
|
+
if (inner instanceof z2.ZodArray) return "JSON string (array)";
|
|
195
|
+
return "unknown";
|
|
196
|
+
}
|
|
197
|
+
function describeReadmeDesc(schema) {
|
|
198
|
+
const baseDesc = schema.description ?? "";
|
|
199
|
+
let inner = schema;
|
|
200
|
+
let defaultValue;
|
|
201
|
+
let isOptional = false;
|
|
202
|
+
while (inner instanceof z2.ZodOptional || inner instanceof z2.ZodDefault) {
|
|
203
|
+
if (inner instanceof z2.ZodDefault) {
|
|
204
|
+
const raw = inner.def.defaultValue;
|
|
205
|
+
defaultValue = typeof raw === "function" ? raw() : raw;
|
|
206
|
+
}
|
|
207
|
+
if (inner instanceof z2.ZodOptional) isOptional = true;
|
|
208
|
+
inner = inner.unwrap();
|
|
209
|
+
}
|
|
210
|
+
if (defaultValue !== void 0) return `${baseDesc} (default: \`${String(defaultValue)}\`)`;
|
|
211
|
+
if (isOptional) return `${baseDesc} (optional)`;
|
|
212
|
+
return baseDesc;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/tools/text.ts
|
|
216
|
+
import { z as z3 } from "zod";
|
|
33
217
|
|
|
34
218
|
// src/cn.ts
|
|
35
219
|
function cn(...classes) {
|
|
@@ -42,31 +226,43 @@ var tools = {
|
|
|
42
226
|
name: "cn",
|
|
43
227
|
description: "Merges class names, filtering out falsy values",
|
|
44
228
|
inputSchema: {
|
|
45
|
-
classes:
|
|
229
|
+
classes: z3.array(z3.string()).describe("List of class names to merge")
|
|
46
230
|
},
|
|
47
|
-
handler: async ({ classes }) => text(cn(...classes))
|
|
231
|
+
handler: async ({ classes }) => text(cn(...classes)),
|
|
232
|
+
examples: [
|
|
233
|
+
{ args: [`'["btn","active","large"]'`], result: "btn active large" }
|
|
234
|
+
]
|
|
48
235
|
}),
|
|
49
236
|
caseConvertTool: toolDef({
|
|
50
237
|
name: "case_convert",
|
|
51
238
|
description: "Converts text to the specified case format",
|
|
52
239
|
inputSchema: {
|
|
53
|
-
input:
|
|
54
|
-
to:
|
|
240
|
+
input: z3.string().describe("Text to convert"),
|
|
241
|
+
to: z3.enum(["upper", "lower", "capitalize", "camel", "snake", "kebab"]).describe("Target case format")
|
|
55
242
|
},
|
|
56
|
-
handler: async ({ input, to }) => text(convert(input, to))
|
|
243
|
+
handler: async ({ input, to }) => text(convert(input, to)),
|
|
244
|
+
examples: [
|
|
245
|
+
{ args: [`"hello world"`, "camel"], result: "helloWorld" },
|
|
246
|
+
{ args: [`"helloWorld"`, "snake"], result: "hello_world" },
|
|
247
|
+
{ args: [`"hello world"`, "kebab"], result: "hello-world" }
|
|
248
|
+
]
|
|
57
249
|
}),
|
|
58
250
|
truncateTool: toolDef({
|
|
59
251
|
name: "truncate",
|
|
60
252
|
description: "Truncates text to a maximum length and appends a suffix",
|
|
61
253
|
inputSchema: {
|
|
62
|
-
input:
|
|
63
|
-
maxLength:
|
|
64
|
-
suffix:
|
|
254
|
+
input: z3.string().describe("Text to truncate"),
|
|
255
|
+
maxLength: z3.number().int().positive().describe("Maximum character length"),
|
|
256
|
+
suffix: z3.string().default("...").describe("Suffix to append when truncated")
|
|
65
257
|
},
|
|
66
258
|
handler: async ({ input, maxLength, suffix }) => {
|
|
67
259
|
const result = input.length <= maxLength ? input : input.slice(0, maxLength - suffix.length) + suffix;
|
|
68
260
|
return text(result);
|
|
69
|
-
}
|
|
261
|
+
},
|
|
262
|
+
examples: [
|
|
263
|
+
{ args: [`"hello world long text"`, "10"], result: "hello w..." },
|
|
264
|
+
{ args: [`"hello world"`, "8", `"\u2026"`], result: "hello w\u2026" }
|
|
265
|
+
]
|
|
70
266
|
})
|
|
71
267
|
};
|
|
72
268
|
var cnTool = defineTool(tools.cnTool);
|
|
@@ -96,7 +292,15 @@ function createUtilsServer() {
|
|
|
96
292
|
[cnTool, caseConvertTool, truncateTool]
|
|
97
293
|
);
|
|
98
294
|
}
|
|
295
|
+
var server = createUtilsServer();
|
|
296
|
+
startServer(server).catch((err) => {
|
|
297
|
+
console.error("[utils] server error:", err);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
});
|
|
99
300
|
export {
|
|
100
301
|
cn,
|
|
101
|
-
createUtilsServer
|
|
302
|
+
createUtilsServer,
|
|
303
|
+
generateReadmeSkills,
|
|
304
|
+
generateSkillMarkdown,
|
|
305
|
+
tools
|
|
102
306
|
};
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../common/
|
|
3
|
+
// ../common/kit/tool.ts
|
|
4
4
|
function toolDef(def) {
|
|
5
5
|
return def;
|
|
6
6
|
}
|
|
@@ -11,32 +11,35 @@ function text(content) {
|
|
|
11
11
|
return { content: [{ type: "text", text: content }] };
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// ../common/
|
|
14
|
+
// ../common/kit/server.ts
|
|
15
15
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
16
16
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
17
|
function createMcpServer(config, tools2) {
|
|
18
|
-
const
|
|
18
|
+
const server3 = new McpServer(config);
|
|
19
19
|
for (const tool of tools2) {
|
|
20
|
-
|
|
20
|
+
server3.registerTool(
|
|
21
21
|
tool.name,
|
|
22
22
|
{ description: tool.description, inputSchema: tool.inputSchema },
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
24
|
tool.handler
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
|
-
return
|
|
27
|
+
return server3;
|
|
28
28
|
}
|
|
29
|
-
async function startServer(
|
|
29
|
+
async function startServer(server3) {
|
|
30
30
|
const transport = new StdioServerTransport();
|
|
31
|
-
await
|
|
31
|
+
await server3.connect(transport);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// ../common/
|
|
34
|
+
// ../common/kit/cli.ts
|
|
35
35
|
import { z } from "zod";
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// ../common/kit/skill.ts
|
|
38
38
|
import { z as z2 } from "zod";
|
|
39
39
|
|
|
40
|
+
// src/tools/text.ts
|
|
41
|
+
import { z as z3 } from "zod";
|
|
42
|
+
|
|
40
43
|
// src/cn.ts
|
|
41
44
|
function cn(...classes) {
|
|
42
45
|
return classes.filter(Boolean).join(" ");
|
|
@@ -48,31 +51,43 @@ var tools = {
|
|
|
48
51
|
name: "cn",
|
|
49
52
|
description: "Merges class names, filtering out falsy values",
|
|
50
53
|
inputSchema: {
|
|
51
|
-
classes:
|
|
54
|
+
classes: z3.array(z3.string()).describe("List of class names to merge")
|
|
52
55
|
},
|
|
53
|
-
handler: async ({ classes }) => text(cn(...classes))
|
|
56
|
+
handler: async ({ classes }) => text(cn(...classes)),
|
|
57
|
+
examples: [
|
|
58
|
+
{ args: [`'["btn","active","large"]'`], result: "btn active large" }
|
|
59
|
+
]
|
|
54
60
|
}),
|
|
55
61
|
caseConvertTool: toolDef({
|
|
56
62
|
name: "case_convert",
|
|
57
63
|
description: "Converts text to the specified case format",
|
|
58
64
|
inputSchema: {
|
|
59
|
-
input:
|
|
60
|
-
to:
|
|
65
|
+
input: z3.string().describe("Text to convert"),
|
|
66
|
+
to: z3.enum(["upper", "lower", "capitalize", "camel", "snake", "kebab"]).describe("Target case format")
|
|
61
67
|
},
|
|
62
|
-
handler: async ({ input, to }) => text(convert(input, to))
|
|
68
|
+
handler: async ({ input, to }) => text(convert(input, to)),
|
|
69
|
+
examples: [
|
|
70
|
+
{ args: [`"hello world"`, "camel"], result: "helloWorld" },
|
|
71
|
+
{ args: [`"helloWorld"`, "snake"], result: "hello_world" },
|
|
72
|
+
{ args: [`"hello world"`, "kebab"], result: "hello-world" }
|
|
73
|
+
]
|
|
63
74
|
}),
|
|
64
75
|
truncateTool: toolDef({
|
|
65
76
|
name: "truncate",
|
|
66
77
|
description: "Truncates text to a maximum length and appends a suffix",
|
|
67
78
|
inputSchema: {
|
|
68
|
-
input:
|
|
69
|
-
maxLength:
|
|
70
|
-
suffix:
|
|
79
|
+
input: z3.string().describe("Text to truncate"),
|
|
80
|
+
maxLength: z3.number().int().positive().describe("Maximum character length"),
|
|
81
|
+
suffix: z3.string().default("...").describe("Suffix to append when truncated")
|
|
71
82
|
},
|
|
72
83
|
handler: async ({ input, maxLength, suffix }) => {
|
|
73
84
|
const result = input.length <= maxLength ? input : input.slice(0, maxLength - suffix.length) + suffix;
|
|
74
85
|
return text(result);
|
|
75
|
-
}
|
|
86
|
+
},
|
|
87
|
+
examples: [
|
|
88
|
+
{ args: [`"hello world long text"`, "10"], result: "hello w..." },
|
|
89
|
+
{ args: [`"hello world"`, "8", `"\u2026"`], result: "hello w\u2026" }
|
|
90
|
+
]
|
|
76
91
|
})
|
|
77
92
|
};
|
|
78
93
|
var cnTool = defineTool(tools.cnTool);
|
|
@@ -102,10 +117,15 @@ function createUtilsServer() {
|
|
|
102
117
|
[cnTool, caseConvertTool, truncateTool]
|
|
103
118
|
);
|
|
104
119
|
}
|
|
105
|
-
|
|
106
|
-
// src/server.ts
|
|
107
120
|
var server = createUtilsServer();
|
|
108
121
|
startServer(server).catch((err) => {
|
|
109
122
|
console.error("[utils] server error:", err);
|
|
110
123
|
process.exit(1);
|
|
111
124
|
});
|
|
125
|
+
|
|
126
|
+
// src/server.ts
|
|
127
|
+
var server2 = createUtilsServer();
|
|
128
|
+
startServer(server2).catch((err) => {
|
|
129
|
+
console.error("[utils] server error:", err);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mono-rele2-utils
|
|
3
|
+
description: Use this skill to invoke text utility functions via the mono-rele2-utils CLI. Handles class name merging, case conversion, and text truncation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# mono-rele2-utils
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
mono-rele2-utils <skillName> [...args]
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Skills
|
|
13
|
+
|
|
14
|
+
### cnTool
|
|
15
|
+
|
|
16
|
+
Merges class names, filtering out falsy values
|
|
17
|
+
|
|
18
|
+
| arg | description |
|
|
19
|
+
|-----|-------------|
|
|
20
|
+
| `classes` | List of class names to merge |
|
|
21
|
+
|
|
22
|
+
### caseConvertTool
|
|
23
|
+
|
|
24
|
+
Converts text to the specified case format
|
|
25
|
+
|
|
26
|
+
| arg | description |
|
|
27
|
+
|-----|-------------|
|
|
28
|
+
| `input` | Text to convert |
|
|
29
|
+
| `to` | Target case format — `upper` \| `lower` \| `capitalize` \| `camel` \| `snake` \| `kebab` |
|
|
30
|
+
|
|
31
|
+
### truncateTool
|
|
32
|
+
|
|
33
|
+
Truncates text to a maximum length and appends a suffix
|
|
34
|
+
|
|
35
|
+
| arg | description |
|
|
36
|
+
|-----|-------------|
|
|
37
|
+
| `input` | Text to truncate |
|
|
38
|
+
| `maxLength` | Maximum character length |
|
|
39
|
+
| `suffix` | Suffix to append when truncated — default: `...` |
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
- `mono-rele2-utils cnTool '["btn","active","large"]'` => `btn active large`
|
|
44
|
+
- `mono-rele2-utils caseConvertTool "hello world" camel` => `helloWorld`
|
|
45
|
+
- `mono-rele2-utils caseConvertTool "helloWorld" snake` => `hello_world`
|
|
46
|
+
- `mono-rele2-utils caseConvertTool "hello world" kebab` => `hello-world`
|
|
47
|
+
- `mono-rele2-utils truncateTool "hello world long text" 10` => `hello w...`
|
|
48
|
+
- `mono-rele2-utils truncateTool "hello world" 8 "…"` => `hello w…`
|
|
49
|
+
|
|
50
|
+
## Guidelines
|
|
51
|
+
|
|
52
|
+
- Arguments are positional — pass them in the order listed in each skill's table
|
|
53
|
+
- Numeric args are auto-parsed — pass as plain numbers (e.g. `10`)
|
|
54
|
+
- Array args must be valid JSON — wrap in single quotes on Unix shells (e.g. `'["a","b"]'`)
|
|
55
|
+
- Optional args with defaults may be omitted
|
|
56
|
+
- Run `mono-rele2-utils` with no args to list all available skills
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@julong/mono-rele2-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "Use this skill to invoke text utility functions via the mono-rele2-utils CLI. Handles class name merging, case conversion, and text truncation.",
|
|
4
5
|
"license": "ISC",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"exports": {
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: mono-rele2-utils
|
|
3
|
-
description: Use this skill to invoke text utility functions via the mono-rele2-utils CLI. Handles class name merging, case conversion, and text truncation.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# mono-rele2-utils
|
|
7
|
-
|
|
8
|
-
CLI for running text utility functions from the mono-rele2 package.
|
|
9
|
-
|
|
10
|
-
```sh
|
|
11
|
-
mono-rele2-utils <skillName> [...args]
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Skills
|
|
15
|
-
|
|
16
|
-
### caseConvertTool
|
|
17
|
-
|
|
18
|
-
Converts text to the specified case format.
|
|
19
|
-
|
|
20
|
-
| arg | description |
|
|
21
|
-
|-----|-------------|
|
|
22
|
-
| `input` | Text to convert |
|
|
23
|
-
| `to` | Target format: `upper` \| `lower` \| `capitalize` \| `camel` \| `snake` \| `kebab` |
|
|
24
|
-
|
|
25
|
-
### cnTool
|
|
26
|
-
|
|
27
|
-
Merges class names, filtering out falsy values.
|
|
28
|
-
|
|
29
|
-
| arg | description |
|
|
30
|
-
|-----|-------------|
|
|
31
|
-
| `classes` | JSON array of class name strings, e.g. `'["btn","active"]'` |
|
|
32
|
-
|
|
33
|
-
### truncateTool
|
|
34
|
-
|
|
35
|
-
Truncates text to a maximum length and appends a suffix.
|
|
36
|
-
|
|
37
|
-
| arg | description |
|
|
38
|
-
|-----|-------------|
|
|
39
|
-
| `input` | Text to truncate |
|
|
40
|
-
| `maxLength` | Maximum character length |
|
|
41
|
-
| `suffix` | Suffix to append when truncated (default: `...`) |
|
|
42
|
-
|
|
43
|
-
## Examples
|
|
44
|
-
|
|
45
|
-
- `mono-rele2-utils caseConvertTool "hello world" camel` → `helloWorld`
|
|
46
|
-
- `mono-rele2-utils caseConvertTool "helloWorld" snake` → `hello_world`
|
|
47
|
-
- `mono-rele2-utils caseConvertTool "hello world" kebab` → `hello-world`
|
|
48
|
-
- `mono-rele2-utils truncateTool "hello world long text" 10` → `hello w...`
|
|
49
|
-
- `mono-rele2-utils truncateTool "hello world" 8 "…"` → `hello w…`
|
|
50
|
-
- `mono-rele2-utils cnTool '["btn","active","large"]'` → `btn active large`
|
|
51
|
-
|
|
52
|
-
## Guidelines
|
|
53
|
-
|
|
54
|
-
- Arguments are positional — pass them in the order listed in each skill's table
|
|
55
|
-
- Numeric args (e.g. `maxLength`) are auto-parsed — pass as plain numbers: `10`
|
|
56
|
-
- Array args (e.g. `classes`) must be valid JSON — wrap in single quotes on Unix shells
|
|
57
|
-
- Optional args with defaults (e.g. `suffix`) may be omitted
|
|
58
|
-
- Run `mono-rele2-utils` with no args to list all available skills
|