revxl-devtools 1.0.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.
Files changed (70) hide show
  1. package/README.md +84 -0
  2. package/checkout/index.html +195 -0
  3. package/dist/auth.d.ts +3 -0
  4. package/dist/auth.js +77 -0
  5. package/dist/codegen/cron-codegen.d.ts +1 -0
  6. package/dist/codegen/cron-codegen.js +56 -0
  7. package/dist/codegen/regex-codegen.d.ts +1 -0
  8. package/dist/codegen/regex-codegen.js +125 -0
  9. package/dist/index.d.ts +22 -0
  10. package/dist/index.js +100 -0
  11. package/dist/registry.d.ts +10 -0
  12. package/dist/registry.js +13 -0
  13. package/dist/tools/base64.d.ts +1 -0
  14. package/dist/tools/base64.js +29 -0
  15. package/dist/tools/batch.d.ts +1 -0
  16. package/dist/tools/batch.js +56 -0
  17. package/dist/tools/chmod.d.ts +1 -0
  18. package/dist/tools/chmod.js +115 -0
  19. package/dist/tools/cron.d.ts +1 -0
  20. package/dist/tools/cron.js +311 -0
  21. package/dist/tools/hash.d.ts +1 -0
  22. package/dist/tools/hash.js +25 -0
  23. package/dist/tools/http-status.d.ts +1 -0
  24. package/dist/tools/http-status.js +59 -0
  25. package/dist/tools/json-diff.d.ts +1 -0
  26. package/dist/tools/json-diff.js +131 -0
  27. package/dist/tools/json-format.d.ts +1 -0
  28. package/dist/tools/json-format.js +38 -0
  29. package/dist/tools/json-query.d.ts +1 -0
  30. package/dist/tools/json-query.js +114 -0
  31. package/dist/tools/jwt.d.ts +1 -0
  32. package/dist/tools/jwt.js +177 -0
  33. package/dist/tools/regex.d.ts +1 -0
  34. package/dist/tools/regex.js +116 -0
  35. package/dist/tools/secrets-scan.d.ts +1 -0
  36. package/dist/tools/secrets-scan.js +173 -0
  37. package/dist/tools/sql-format.d.ts +1 -0
  38. package/dist/tools/sql-format.js +157 -0
  39. package/dist/tools/timestamp.d.ts +1 -0
  40. package/dist/tools/timestamp.js +72 -0
  41. package/dist/tools/url-encode.d.ts +1 -0
  42. package/dist/tools/url-encode.js +26 -0
  43. package/dist/tools/uuid.d.ts +1 -0
  44. package/dist/tools/uuid.js +24 -0
  45. package/dist/tools/yaml-convert.d.ts +1 -0
  46. package/dist/tools/yaml-convert.js +371 -0
  47. package/package.json +29 -0
  48. package/src/auth.ts +99 -0
  49. package/src/codegen/cron-codegen.ts +66 -0
  50. package/src/codegen/regex-codegen.ts +132 -0
  51. package/src/index.ts +134 -0
  52. package/src/registry.ts +25 -0
  53. package/src/tools/base64.ts +32 -0
  54. package/src/tools/batch.ts +69 -0
  55. package/src/tools/chmod.ts +133 -0
  56. package/src/tools/cron.ts +365 -0
  57. package/src/tools/hash.ts +26 -0
  58. package/src/tools/http-status.ts +63 -0
  59. package/src/tools/json-diff.ts +153 -0
  60. package/src/tools/json-format.ts +43 -0
  61. package/src/tools/json-query.ts +126 -0
  62. package/src/tools/jwt.ts +193 -0
  63. package/src/tools/regex.ts +131 -0
  64. package/src/tools/secrets-scan.ts +212 -0
  65. package/src/tools/sql-format.ts +178 -0
  66. package/src/tools/timestamp.ts +74 -0
  67. package/src/tools/url-encode.ts +29 -0
  68. package/src/tools/uuid.ts +25 -0
  69. package/src/tools/yaml-convert.ts +383 -0
  70. package/tsconfig.json +14 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @revxl/devtools
2
+
3
+ 17 developer tools for AI agents. Works with Claude Desktop, Cursor, Windsurf, VS Code.
4
+
5
+ ## Install
6
+
7
+ Add to your MCP client config:
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "devtools": {
13
+ "command": "npx",
14
+ "args": ["-y", "@revxl/devtools"]
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ ## Why @revxl/devtools?
21
+
22
+ | Feature | mcp-devutils | @revxl/devtools |
23
+ |---------|-------------|-----------------|
24
+ | Price | $5 one-time | $7 one-time |
25
+ | Free tools | 15 (commodity) | 7 (unlimited) |
26
+ | Regex to code generation | No | Yes (Python, JS, Go, Rust, Java) |
27
+ | Batch operations | No | Yes (500 items per call) |
28
+ | Secrets scanner | No | Yes (AWS, GitHub, Stripe, OpenAI) |
29
+ | Cron from English | No | Yes ("every weekday at 9am") |
30
+ | JSON diff | Pro only | Pro with path-level changes |
31
+ | Context token usage | 44 tool defs | 17 tool defs (saves ~2K tokens) |
32
+
33
+ ## Free Tools (7)
34
+
35
+ | Tool | Description |
36
+ |------|-------------|
37
+ | `json_format` | Format, minify, or validate JSON strings |
38
+ | `base64` | Encode or decode Base64 strings |
39
+ | `url_encode` | URL encode or decode strings |
40
+ | `uuid_generate` | Generate one or more v4 UUIDs |
41
+ | `hash_text` | Hash text with MD5, SHA-256, or SHA-512 |
42
+ | `timestamp` | Convert between Unix timestamps, ISO 8601, and human-readable dates |
43
+ | `http_status` | Look up HTTP status code name and category |
44
+
45
+ ## Pro Tools (10)
46
+
47
+ Each Pro tool has 3 free trials before purchase.
48
+
49
+ | Tool | Description |
50
+ |------|-------------|
51
+ | `jwt` | Decode and inspect JWTs, or create signed JWTs with custom claims |
52
+ | `regex` | Test regex patterns with match highlighting + generate code in 5 languages |
53
+ | `cron` | Explain cron expressions, compute next runs, or generate cron from plain English |
54
+ | `json_diff` | Deep-diff two JSON objects with path-level added/removed/changed detail |
55
+ | `json_query` | Query JSON with dot-notation and bracket paths (e.g. `users[0].name`) |
56
+ | `batch` | Run any free tool on up to 500 inputs in a single call |
57
+ | `sql_format` | Format and prettify SQL queries with proper indentation |
58
+ | `yaml_convert` | Convert between JSON and YAML |
59
+ | `chmod` | Explain or generate Unix file permissions from symbolic or numeric notation |
60
+ | `secrets_scan` | Scan text for leaked secrets (AWS keys, GitHub tokens, Stripe keys, OpenAI keys) |
61
+
62
+ ## Get Pro
63
+
64
+ 1. Purchase at [https://revxl-devtools.vercel.app](https://revxl-devtools.vercel.app) — **$7 one-time**
65
+ 2. You'll receive a license key (format: `REVXL-XXXX-XXXX-XXXX`)
66
+ 3. Add it to your MCP config:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "devtools": {
72
+ "command": "npx",
73
+ "args": ["-y", "@revxl/devtools"],
74
+ "env": {
75
+ "REVXL_PRO_KEY": "REVXL-XXXX-XXXX-XXXX"
76
+ }
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ## License
83
+
84
+ MIT — RevXL
@@ -0,0 +1,195 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>@revxl/devtools Pro — Lifetime Access</title>
7
+ <style>
8
+ *, *::before, *::after {
9
+ box-sizing: border-box;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ body {
15
+ background: #000;
16
+ color: #ededed;
17
+ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
18
+ min-height: 100vh;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ padding: 24px 16px;
23
+ }
24
+
25
+ .card {
26
+ background: #111;
27
+ border: 1px solid #222;
28
+ border-radius: 16px;
29
+ max-width: 480px;
30
+ width: 100%;
31
+ padding: 40px 36px;
32
+ }
33
+
34
+ .badge {
35
+ display: inline-block;
36
+ background: #22C55E18;
37
+ color: #22C55E;
38
+ border: 1px solid #22C55E40;
39
+ border-radius: 20px;
40
+ font-size: 12px;
41
+ font-weight: 600;
42
+ letter-spacing: 0.05em;
43
+ padding: 4px 12px;
44
+ margin-bottom: 20px;
45
+ text-transform: uppercase;
46
+ }
47
+
48
+ h1 {
49
+ font-size: 24px;
50
+ font-weight: 700;
51
+ color: #ededed;
52
+ letter-spacing: -0.02em;
53
+ margin-bottom: 24px;
54
+ line-height: 1.2;
55
+ }
56
+
57
+ .price-block {
58
+ margin-bottom: 28px;
59
+ }
60
+
61
+ .price {
62
+ font-size: 56px;
63
+ font-weight: 800;
64
+ color: #22C55E;
65
+ line-height: 1;
66
+ letter-spacing: -0.03em;
67
+ }
68
+
69
+ .price-note {
70
+ font-size: 14px;
71
+ color: #888;
72
+ margin-top: 6px;
73
+ }
74
+
75
+ .divider {
76
+ border: none;
77
+ border-top: 1px solid #222;
78
+ margin: 28px 0;
79
+ }
80
+
81
+ .features-label {
82
+ font-size: 11px;
83
+ font-weight: 600;
84
+ letter-spacing: 0.08em;
85
+ text-transform: uppercase;
86
+ color: #555;
87
+ margin-bottom: 14px;
88
+ }
89
+
90
+ .features {
91
+ list-style: none;
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: 10px;
95
+ margin-bottom: 32px;
96
+ }
97
+
98
+ .features li {
99
+ display: flex;
100
+ align-items: flex-start;
101
+ gap: 10px;
102
+ font-size: 14px;
103
+ color: #ccc;
104
+ line-height: 1.4;
105
+ }
106
+
107
+ .features li .check {
108
+ color: #22C55E;
109
+ font-size: 15px;
110
+ flex-shrink: 0;
111
+ margin-top: 1px;
112
+ }
113
+
114
+ .cta {
115
+ display: block;
116
+ width: 100%;
117
+ background: #22C55E;
118
+ color: #000;
119
+ font-size: 16px;
120
+ font-weight: 700;
121
+ text-align: center;
122
+ text-decoration: none;
123
+ border: none;
124
+ border-radius: 10px;
125
+ padding: 14px 24px;
126
+ cursor: pointer;
127
+ transition: background 0.15s ease, transform 0.1s ease;
128
+ letter-spacing: -0.01em;
129
+ }
130
+
131
+ .cta:hover {
132
+ background: #16a34a;
133
+ }
134
+
135
+ .cta:active {
136
+ transform: scale(0.98);
137
+ }
138
+
139
+ .stripe-note {
140
+ text-align: center;
141
+ font-size: 12px;
142
+ color: #555;
143
+ margin-top: 16px;
144
+ }
145
+
146
+ .stripe-note span {
147
+ color: #777;
148
+ }
149
+ </style>
150
+ </head>
151
+ <body>
152
+ <div class="card">
153
+ <div class="badge">Pro</div>
154
+ <h1>@revxl/devtools Pro</h1>
155
+
156
+ <div class="price-block">
157
+ <div class="price">$7</div>
158
+ <div class="price-note">One-time payment. Lifetime access.</div>
159
+ </div>
160
+
161
+ <hr class="divider" />
162
+
163
+ <div class="features-label">Everything included</div>
164
+ <ul class="features">
165
+ <li><span class="check">&#10003;</span> JWT decode, verify, and create</li>
166
+ <li><span class="check">&#10003;</span> Regex tester + code gen (Python, JS, Go, Rust, Java)</li>
167
+ <li><span class="check">&#10003;</span> Cron from natural language</li>
168
+ <li><span class="check">&#10003;</span> JSON diff + JSONPath queries</li>
169
+ <li><span class="check">&#10003;</span> Batch operations (500 items)</li>
170
+ <li><span class="check">&#10003;</span> SQL formatter (PostgreSQL, MySQL, SQLite)</li>
171
+ <li><span class="check">&#10003;</span> YAML / JSON / TOML converter</li>
172
+ <li><span class="check">&#10003;</span> File permission calculator</li>
173
+ <li><span class="check">&#10003;</span> Secrets scanner (AWS, GitHub, Stripe, OpenAI)</li>
174
+ </ul>
175
+
176
+ <a class="cta" href="#" id="checkout-btn">Get Pro Key</a>
177
+
178
+ <p class="stripe-note">
179
+ <span>Powered by Stripe.</span> Key delivered instantly after payment.
180
+ </p>
181
+ </div>
182
+
183
+ <script>
184
+ // Replace with your Stripe Payment Link URL
185
+ const STRIPE_PAYMENT_LINK = '#';
186
+
187
+ document.getElementById('checkout-btn').addEventListener('click', function (e) {
188
+ if (STRIPE_PAYMENT_LINK && STRIPE_PAYMENT_LINK !== '#') {
189
+ e.preventDefault();
190
+ window.location.href = STRIPE_PAYMENT_LINK;
191
+ }
192
+ });
193
+ </script>
194
+ </body>
195
+ </html>
package/dist/auth.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function checkProAccess(): Promise<boolean>;
2
+ export declare function getTrialUsesRemaining(toolName: string): number;
3
+ export declare function incrementTrialUse(toolName: string): void;
package/dist/auth.js ADDED
@@ -0,0 +1,77 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ const CACHE_PATH = join(homedir(), ".revxl-devtools-cache.json");
5
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
6
+ const MAX_TRIAL_USES = 3;
7
+ function loadCache() {
8
+ try {
9
+ const raw = readFileSync(CACHE_PATH, "utf-8");
10
+ return JSON.parse(raw);
11
+ }
12
+ catch {
13
+ return { proValidated: false, validatedAt: 0, trialUses: {} };
14
+ }
15
+ }
16
+ function saveCache(cache) {
17
+ try {
18
+ writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2), "utf-8");
19
+ }
20
+ catch {
21
+ // Silently fail — cache is best-effort
22
+ }
23
+ }
24
+ async function validateKeyWithSupabase(key) {
25
+ const supabaseUrl = process.env.SUPABASE_URL;
26
+ const supabaseAnonKey = process.env.SUPABASE_ANON_KEY;
27
+ if (!supabaseUrl || !supabaseAnonKey) {
28
+ // Dev mode: accept any RX-prefixed key
29
+ return key.startsWith("RX.");
30
+ }
31
+ try {
32
+ const res = await fetch(`${supabaseUrl}/rest/v1/pro_keys?key=eq.${encodeURIComponent(key)}&select=active`, {
33
+ headers: {
34
+ apikey: supabaseAnonKey,
35
+ Authorization: `Bearer ${supabaseAnonKey}`,
36
+ },
37
+ });
38
+ if (!res.ok) {
39
+ // Supabase down — fall back to format check
40
+ return key.startsWith("RX.");
41
+ }
42
+ const rows = (await res.json());
43
+ return rows.length > 0 && rows[0].active === true;
44
+ }
45
+ catch {
46
+ // Network error — fall back to format check
47
+ return key.startsWith("RX.");
48
+ }
49
+ }
50
+ export async function checkProAccess() {
51
+ const key = process.env.MCP_DEVTOOLS_KEY;
52
+ if (!key || !key.startsWith("RX.")) {
53
+ return false;
54
+ }
55
+ const cache = loadCache();
56
+ // Check cache validity
57
+ const cacheAge = Date.now() - cache.validatedAt;
58
+ if (cache.proValidated && cacheAge < CACHE_TTL_MS) {
59
+ return true;
60
+ }
61
+ // Validate against Supabase (or dev mode fallback)
62
+ const valid = await validateKeyWithSupabase(key);
63
+ cache.proValidated = valid;
64
+ cache.validatedAt = Date.now();
65
+ saveCache(cache);
66
+ return valid;
67
+ }
68
+ export function getTrialUsesRemaining(toolName) {
69
+ const cache = loadCache();
70
+ const used = cache.trialUses[toolName] ?? 0;
71
+ return Math.max(0, MAX_TRIAL_USES - used);
72
+ }
73
+ export function incrementTrialUse(toolName) {
74
+ const cache = loadCache();
75
+ cache.trialUses[toolName] = (cache.trialUses[toolName] ?? 0) + 1;
76
+ saveCache(cache);
77
+ }
@@ -0,0 +1 @@
1
+ export declare function generateCronCode(expression: string): string;
@@ -0,0 +1,56 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Cron code generation — crontab, systemd timer, and node-cron snippets
3
+ // ---------------------------------------------------------------------------
4
+ export function generateCronCode(expression) {
5
+ const crontab = `# Crontab entry
6
+ ${expression} /path/to/command`;
7
+ const systemd = generateSystemdTimer(expression);
8
+ const nodeCron = generateNodeCron(expression);
9
+ return [crontab, systemd, nodeCron].join("\n\n---\n\n");
10
+ }
11
+ function cronToOnCalendar(expression) {
12
+ const parts = expression.split(/\s+/);
13
+ if (parts.length !== 5)
14
+ return expression;
15
+ const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
16
+ const dayMap = {
17
+ "0": "Sun", "1": "Mon", "2": "Tue", "3": "Wed",
18
+ "4": "Thu", "5": "Fri", "6": "Sat", "7": "Sun",
19
+ };
20
+ let dow = "*";
21
+ if (dayOfWeek !== "*") {
22
+ dow = dayOfWeek
23
+ .split(",")
24
+ .map((d) => dayMap[d] || d)
25
+ .join(",");
26
+ }
27
+ const dom = dayOfMonth === "*" ? "*" : dayOfMonth;
28
+ const mon = month === "*" ? "*" : month.padStart(2, "0");
29
+ const h = hour === "*" ? "*" : hour.padStart(2, "0");
30
+ const m = minute === "*" ? "*" : minute.padStart(2, "0");
31
+ return `${dow} *-${mon}-${dom} ${h}:${m}:00`;
32
+ }
33
+ function generateSystemdTimer(expression) {
34
+ const onCalendar = cronToOnCalendar(expression);
35
+ return `# systemd timer unit — save as /etc/systemd/system/mytask.timer
36
+ [Unit]
37
+ Description=My scheduled task
38
+
39
+ [Timer]
40
+ OnCalendar=${onCalendar}
41
+ Persistent=true
42
+
43
+ [Install]
44
+ WantedBy=timers.target`;
45
+ }
46
+ function generateNodeCron(expression) {
47
+ return `// Node.js — npm install cron
48
+ import { CronJob } from 'cron';
49
+
50
+ const job = new CronJob('${expression}', () => {
51
+ console.log('Task executed at', new Date().toISOString());
52
+ // your logic here
53
+ });
54
+
55
+ job.start();`;
56
+ }
@@ -0,0 +1 @@
1
+ export declare function generateRegexCode(pattern: string, flags: string, languages?: string[]): string;
@@ -0,0 +1,125 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Regex code generation — produces working snippets in 5 languages
3
+ // ---------------------------------------------------------------------------
4
+ const ALL_LANGUAGES = ["javascript", "python", "go", "rust", "java"];
5
+ function jsFlags(flags) {
6
+ return flags || "";
7
+ }
8
+ function pyFlags(flags) {
9
+ const parts = [];
10
+ if (flags.includes("i"))
11
+ parts.push("re.IGNORECASE");
12
+ if (flags.includes("m"))
13
+ parts.push("re.MULTILINE");
14
+ if (flags.includes("s"))
15
+ parts.push("re.DOTALL");
16
+ return parts.length ? ", " + parts.join(" | ") : "";
17
+ }
18
+ function generateJS(pattern, flags) {
19
+ const f = jsFlags(flags);
20
+ return `// JavaScript
21
+ const regex = /${pattern}/${f};
22
+ const text = "your text here";
23
+
24
+ const matches = text.match(regex);
25
+ if (matches) {
26
+ console.log("Matches:", matches);
27
+ } else {
28
+ console.log("No matches found");
29
+ }`;
30
+ }
31
+ function generatePython(pattern, flags) {
32
+ const f = pyFlags(flags);
33
+ return `# Python
34
+ import re
35
+
36
+ pattern = re.compile(r'${pattern}'${f})
37
+ text = "your text here"
38
+
39
+ matches = pattern.findall(text)
40
+ print("Matches:", matches)`;
41
+ }
42
+ function generateGo(pattern, flags) {
43
+ // Go uses inline flags (?i) etc.
44
+ let goFlags = "";
45
+ if (flags.includes("i"))
46
+ goFlags += "i";
47
+ if (flags.includes("m"))
48
+ goFlags += "m";
49
+ if (flags.includes("s"))
50
+ goFlags += "s";
51
+ const prefix = goFlags ? `(?${goFlags})` : "";
52
+ return `// Go
53
+ package main
54
+
55
+ import (
56
+ \t"fmt"
57
+ \t"regexp"
58
+ )
59
+
60
+ func main() {
61
+ \tre := regexp.MustCompile(\`${prefix}${pattern}\`)
62
+ \ttext := "your text here"
63
+
64
+ \tmatches := re.FindAllString(text, -1)
65
+ \tfmt.Println("Matches:", matches)
66
+ }`;
67
+ }
68
+ function generateRust(pattern, flags) {
69
+ let rustFlags = "";
70
+ if (flags.includes("i"))
71
+ rustFlags += "(?i)";
72
+ if (flags.includes("m"))
73
+ rustFlags += "(?m)";
74
+ if (flags.includes("s"))
75
+ rustFlags += "(?s)";
76
+ return `// Rust — add \`regex = "1"\` to Cargo.toml [dependencies]
77
+ use regex::Regex;
78
+
79
+ fn main() {
80
+ let re = Regex::new(r"${rustFlags}${pattern}").unwrap();
81
+ let text = "your text here";
82
+
83
+ for m in re.find_iter(text) {
84
+ println!("Match: {}", m.as_str());
85
+ }
86
+ }`;
87
+ }
88
+ function generateJava(pattern, flags) {
89
+ const javaFlags = [];
90
+ if (flags.includes("i"))
91
+ javaFlags.push("Pattern.CASE_INSENSITIVE");
92
+ if (flags.includes("m"))
93
+ javaFlags.push("Pattern.MULTILINE");
94
+ if (flags.includes("s"))
95
+ javaFlags.push("Pattern.DOTALL");
96
+ const flagArg = javaFlags.length ? ", " + javaFlags.join(" | ") : "";
97
+ return `// Java
98
+ import java.util.regex.Pattern;
99
+ import java.util.regex.Matcher;
100
+
101
+ public class RegexDemo {
102
+ public static void main(String[] args) {
103
+ Pattern pattern = Pattern.compile("${pattern}"${flagArg});
104
+ String text = "your text here";
105
+ Matcher matcher = pattern.matcher(text);
106
+
107
+ while (matcher.find()) {
108
+ System.out.println("Match: " + matcher.group());
109
+ }
110
+ }
111
+ }`;
112
+ }
113
+ const generators = {
114
+ javascript: generateJS,
115
+ python: generatePython,
116
+ go: generateGo,
117
+ rust: generateRust,
118
+ java: generateJava,
119
+ };
120
+ export function generateRegexCode(pattern, flags, languages) {
121
+ const langs = languages && languages.length
122
+ ? languages.map((l) => l.toLowerCase()).filter((l) => l in generators)
123
+ : ALL_LANGUAGES;
124
+ return langs.map((lang) => generators[lang](pattern, flags)).join("\n\n---\n\n");
125
+ }
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import { registerTool, getToolByName, getAllTools } from "./registry.js";
3
+ import type { ToolDefinition } from "./registry.js";
4
+ export { registerTool, getToolByName, getAllTools };
5
+ export type { ToolDefinition };
6
+ import "./tools/json-format.js";
7
+ import "./tools/base64.js";
8
+ import "./tools/url-encode.js";
9
+ import "./tools/uuid.js";
10
+ import "./tools/hash.js";
11
+ import "./tools/timestamp.js";
12
+ import "./tools/http-status.js";
13
+ import "./tools/jwt.js";
14
+ import "./tools/regex.js";
15
+ import "./tools/cron.js";
16
+ import "./tools/json-diff.js";
17
+ import "./tools/json-query.js";
18
+ import "./tools/batch.js";
19
+ import "./tools/sql-format.js";
20
+ import "./tools/yaml-convert.js";
21
+ import "./tools/chmod.js";
22
+ import "./tools/secrets-scan.js";
package/dist/index.js ADDED
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { checkProAccess, getTrialUsesRemaining, incrementTrialUse, } from "./auth.js";
6
+ import { registerTool, getToolByName, getAllTools, } from "./registry.js";
7
+ // Re-export for tool files that import from index
8
+ export { registerTool, getToolByName, getAllTools };
9
+ // ---------------------------------------------------------------------------
10
+ // MCP Server
11
+ // ---------------------------------------------------------------------------
12
+ const PURCHASE_URL = "https://revxl-devtools.vercel.app";
13
+ const server = new Server({ name: "@revxl/devtools", version: "1.0.0" }, { capabilities: { tools: {} } });
14
+ // --- ListTools ------------------------------------------------------------
15
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
16
+ const toolList = getAllTools().map((t) => ({
17
+ name: t.name,
18
+ description: t.pro
19
+ ? `${t.description} [PRO - 3 free trials]`
20
+ : t.description,
21
+ inputSchema: t.inputSchema,
22
+ }));
23
+ return { tools: toolList };
24
+ });
25
+ // --- CallTool -------------------------------------------------------------
26
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
27
+ const { name, arguments: args } = request.params;
28
+ const tool = getToolByName(name);
29
+ if (!tool) {
30
+ return {
31
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
32
+ isError: true,
33
+ };
34
+ }
35
+ // Pro gate
36
+ if (tool.pro) {
37
+ const isPro = await checkProAccess();
38
+ if (!isPro) {
39
+ const remaining = getTrialUsesRemaining(name);
40
+ if (remaining <= 0) {
41
+ return {
42
+ content: [
43
+ {
44
+ type: "text",
45
+ text: `⚡ ${name} is a Pro tool and you've used all 3 free trials.\n\nUpgrade for $7 one-time at ${PURCHASE_URL} to unlock unlimited access to all Pro tools.`,
46
+ },
47
+ ],
48
+ isError: false,
49
+ };
50
+ }
51
+ incrementTrialUse(name);
52
+ }
53
+ }
54
+ try {
55
+ const result = await tool.handler((args ?? {}));
56
+ const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
57
+ return { content: [{ type: "text", text }] };
58
+ }
59
+ catch (err) {
60
+ const message = err instanceof Error ? err.message : String(err);
61
+ return {
62
+ content: [{ type: "text", text: `Error in ${name}: ${message}` }],
63
+ isError: true,
64
+ };
65
+ }
66
+ });
67
+ // ---------------------------------------------------------------------------
68
+ // Tool imports
69
+ // ---------------------------------------------------------------------------
70
+ // Free tools
71
+ import "./tools/json-format.js";
72
+ import "./tools/base64.js";
73
+ import "./tools/url-encode.js";
74
+ import "./tools/uuid.js";
75
+ import "./tools/hash.js";
76
+ import "./tools/timestamp.js";
77
+ import "./tools/http-status.js";
78
+ // Pro tools
79
+ import "./tools/jwt.js";
80
+ import "./tools/regex.js";
81
+ import "./tools/cron.js";
82
+ import "./tools/json-diff.js";
83
+ import "./tools/json-query.js";
84
+ import "./tools/batch.js";
85
+ import "./tools/sql-format.js";
86
+ import "./tools/yaml-convert.js";
87
+ import "./tools/chmod.js";
88
+ import "./tools/secrets-scan.js";
89
+ // ---------------------------------------------------------------------------
90
+ // Start
91
+ // ---------------------------------------------------------------------------
92
+ async function main() {
93
+ const transport = new StdioServerTransport();
94
+ await server.connect(transport);
95
+ console.error("@revxl/devtools MCP server running on stdio");
96
+ }
97
+ main().catch((err) => {
98
+ console.error("Fatal error:", err);
99
+ process.exit(1);
100
+ });
@@ -0,0 +1,10 @@
1
+ export interface ToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ pro: boolean;
5
+ inputSchema: Record<string, unknown>;
6
+ handler: (args: Record<string, unknown>) => Promise<unknown>;
7
+ }
8
+ export declare function registerTool(tool: ToolDefinition): void;
9
+ export declare function getToolByName(name: string): ToolDefinition | undefined;
10
+ export declare function getAllTools(): ToolDefinition[];