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