@mcptoolshop/toolshopstudio 1.4.0-toolshop → 1.5.0-toolshop

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 (69) hide show
  1. package/.github/workflows/ci.yml +8 -3
  2. package/CHANGELOG.md +25 -0
  3. package/Dockerfile +3 -3
  4. package/README.md +44 -4
  5. package/bin/toolshop.mjs +3 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +1 -0
  8. package/dist/registry/auto-docs.d.ts +30 -0
  9. package/dist/registry/auto-docs.js +130 -0
  10. package/dist/registry/cli.d.ts +31 -0
  11. package/dist/registry/cli.js +129 -0
  12. package/dist/registry/consistency.d.ts +44 -0
  13. package/dist/registry/consistency.js +89 -0
  14. package/dist/registry/engine.test.d.ts +1 -0
  15. package/dist/registry/engine.test.js +109 -0
  16. package/dist/registry/full.test.d.ts +1 -0
  17. package/dist/registry/full.test.js +103 -0
  18. package/dist/registry/helpers.d.ts +20 -0
  19. package/dist/registry/helpers.js +39 -0
  20. package/dist/registry/index.d.ts +12 -0
  21. package/dist/registry/index.js +13 -0
  22. package/dist/registry/mcp-server.d.ts +61 -0
  23. package/dist/registry/mcp-server.js +40 -0
  24. package/dist/registry/mcp-tools.d.ts +85 -0
  25. package/dist/registry/mcp-tools.js +76 -0
  26. package/dist/registry/registry.test.d.ts +1 -0
  27. package/dist/registry/registry.test.js +57 -0
  28. package/dist/registry/resource.d.ts +126 -0
  29. package/dist/registry/resource.js +73 -0
  30. package/dist/registry/runtime.d.ts +72 -0
  31. package/dist/registry/runtime.js +43 -0
  32. package/dist/registry/runtime.test.d.ts +1 -0
  33. package/dist/registry/runtime.test.js +129 -0
  34. package/dist/registry/search.d.ts +77 -0
  35. package/dist/registry/search.js +136 -0
  36. package/dist/registry/tools.d.ts +8 -0
  37. package/dist/registry/tools.js +358 -0
  38. package/dist/registry/types.d.ts +400 -0
  39. package/dist/registry/types.js +74 -0
  40. package/dist/registry/validate.d.ts +27 -0
  41. package/dist/registry/validate.js +86 -0
  42. package/docs/presets.md +29 -0
  43. package/docs/registry.md +240 -0
  44. package/docs/tools/ffmpeg.md +46 -0
  45. package/docs/tools/freecad.md +43 -0
  46. package/docs/tools/gdal.md +44 -0
  47. package/docs/tools/openscad.md +45 -0
  48. package/docs/tools/pandoc.md +41 -0
  49. package/package.json +9 -4
  50. package/scripts/release.mjs +1 -1
  51. package/smoke.mjs +24 -1
  52. package/src/index.ts +1 -0
  53. package/src/registry/auto-docs.ts +155 -0
  54. package/src/registry/cli.ts +146 -0
  55. package/src/registry/consistency.ts +117 -0
  56. package/src/registry/engine.test.ts +137 -0
  57. package/src/registry/full.test.ts +120 -0
  58. package/src/registry/helpers.ts +55 -0
  59. package/src/registry/index.ts +13 -0
  60. package/src/registry/mcp-server.ts +49 -0
  61. package/src/registry/mcp-tools.ts +117 -0
  62. package/src/registry/registry.test.ts +75 -0
  63. package/src/registry/resource.ts +94 -0
  64. package/src/registry/runtime.test.ts +154 -0
  65. package/src/registry/runtime.ts +115 -0
  66. package/src/registry/search.ts +179 -0
  67. package/src/registry/tools.ts +374 -0
  68. package/src/registry/types.ts +91 -0
  69. package/src/registry/validate.ts +109 -0
@@ -23,7 +23,7 @@ concurrency:
23
23
 
24
24
  jobs:
25
25
  test:
26
- name: Typecheck + Test + Smoke (FFmpeg + Pandoc + FreeCAD + GDAL + OpenSCAD)
26
+ name: Typecheck + Test + Registry + Smoke
27
27
  runs-on: ubuntu-latest
28
28
 
29
29
  strategy:
@@ -43,11 +43,16 @@ jobs:
43
43
  - name: Typecheck
44
44
  run: npm run typecheck
45
45
 
46
- - name: Unit tests (FFmpeg + Pandoc + FreeCAD + GDAL + OpenSCAD)
46
+ - name: Unit tests (237 5 tools + registry)
47
47
  run: npm test
48
48
 
49
49
  - name: Build
50
50
  run: npm run build
51
51
 
52
- - name: Smoke test (all five tools)
52
+ - name: Registry validation + docs generation
53
+ run: |
54
+ npm run toolshop registry json
55
+ npm run docs:generate
56
+
57
+ - name: Smoke test (5 tools + registry, 13 tests)
53
58
  run: npm run smoke
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.5.0-toolshop] - 2026-02-19
6
+
7
+ ### Added
8
+ - **Live Registry**: self-documenting, validated, MCP-ready registry as first-class citizen
9
+ - Registry Phase 1: type-safe `ToolDefinition` schema, `TOOL_REGISTRY` with all 5 tools, 11 `SHARED_PATTERNS`, search/introspection functions
10
+ - Registry Phase 2: `validateRegistry()` engine with Zod refinements (preset existence, fallback integrity, shared pattern coverage), enriched search (`searchAllByPreset`, `searchByOutputExt`, `getPremiumPresetMap`, `getToolsByPattern`), consistency checks
11
+ - Registry Phase 3: `loadRegistry()` validated singleton, MCP introspection tools (`listTools`, `getToolInfo`, `searchPresets`), CLI (`toolshop registry list|show|summary|json|docs`), auto-docs generator (pure markdown)
12
+ - Registry Phase 4: MCP resources (`getRegistryResource`, `getToolResource`), `generateAllToolDocs()` build step, unified `allRegistryMcpTools` table (5 MCP tools), `json` and `docs` CLI subcommands
13
+ - 7 auto-generated markdown docs committed (5 per-tool + registry.md + presets.md)
14
+ - `npm run docs:generate` script for regenerating docs
15
+ - `npm run toolshop registry` CLI with 5 subcommands
16
+ - CI now validates registry JSON + generates docs on every push
17
+ - 48 new registry tests (8 golden + 15 engine + 16 runtime + 9 integration)
18
+ - Smoke test 13: registry validation + listTools + getRegistryResource
19
+
20
+ ### Changed
21
+ - Total test count: 189 → 237 (48 registry tests)
22
+ - Smoke tests: 12 → 13 (registry smoke block)
23
+ - README updated with registry table, CLI docs, generated docs section, architecture bullet
24
+ - Dockerfile relabeled for v1.5.0-toolshop with registry description
25
+ - CI job name reflects registry presence
26
+ - package.json description highlights live Registry
27
+
28
+ ---
29
+
5
30
  ## [1.4.0-toolshop] - 2026-02-19
6
31
 
7
32
  ### Added
package/Dockerfile CHANGED
@@ -46,9 +46,9 @@ ENV NODE_ENV=production
46
46
 
47
47
  VOLUME ["/sandbox", "/tmp/toolshop"]
48
48
 
49
- LABEL org.opencontainers.image.title="ToolShopStudio" \
50
- org.opencontainers.image.description="FFmpeg YouTube + Pandoc + FreeCAD + GDAL + OpenSCAD MCP tools — quint-tool kit for ordinary creators" \
51
- org.opencontainers.image.version="1.4.0-toolshop" \
49
+ LABEL org.opencontainers.image.title="ToolShopStudio v1.5.0-toolshop" \
50
+ org.opencontainers.image.description="Five MCP tools + live Registry (FFmpeg, Pandoc, FreeCAD, GDAL, OpenSCAD)" \
51
+ org.opencontainers.image.version="1.5.0-toolshop" \
52
52
  org.opencontainers.image.logo="assets/logo.png" \
53
53
  org.opencontainers.image.source="https://github.com/mcp-tool-shop-org/ToolShopStudio" \
54
54
  org.opencontainers.image.licenses="MIT"
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <h1 align="center">ToolShopStudio</h1>
6
6
 
7
7
  <p align="center">
8
- One install. Five production-grade MCP tools for ordinary creators.
8
+ Five MCP tools + live Registry — one install for ordinary creators.
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -23,7 +23,7 @@
23
23
  <a href="https://github.com/mcp-tool-shop-org/ToolShopStudio/actions"><img src="https://github.com/mcp-tool-shop-org/ToolShopStudio/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
24
24
  <a href="https://www.npmjs.com/package/@mcptoolshop/toolshopstudio"><img src="https://img.shields.io/npm/v/@mcptoolshop/toolshopstudio" alt="npm version"></a>
25
25
  <img src="https://img.shields.io/badge/tools-FFmpeg%20%2B%20Pandoc%20%2B%20FreeCAD%20%2B%20GDAL%20%2B%20OpenSCAD-orange" alt="Tools">
26
- <img src="https://img.shields.io/badge/tests-189%20passing-brightgreen" alt="Tests">
26
+ <img src="https://img.shields.io/badge/tests-237%20passing-brightgreen" alt="Tests">
27
27
  <img src="https://img.shields.io/badge/license-MIT-blue" alt="License">
28
28
  </p>
29
29
 
@@ -38,9 +38,31 @@
38
38
  | **FreeCAD MCP** | Safe 3D CAD export: STL, STEP, GLB, 3MF, OBJ — headless, no user code |
39
39
  | **GDAL MCP** | Geospatial transforms: reproject rasters, convert vectors, clip regions — the FFmpeg of GIS |
40
40
  | **OpenSCAD MCP** | Pure-text parametric CAD: STL, OBJ, 3MF, PNG preview, DXF — text in, mesh out |
41
+ | **Registry** | Self-documenting, MCP-ready — 5 tools, 27 presets, 15 output formats, auto-generated docs |
41
42
 
42
43
  All five tools share the same frozen surface: **schema-first, sandboxed, observable, context DI, zero raw args**.
43
44
 
45
+ ToolShopStudio now includes a **self-documenting registry** — ask it anything about any tool, preset, or pattern.
46
+
47
+ ```typescript
48
+ import { registry } from "@mcptoolshop/toolshopstudio";
49
+
50
+ registry.findTool("openscad"); // full ToolDefinition
51
+ registry.searchByPreset("academic-pdf"); // → { toolId: "pandoc", ... }
52
+ registry.searchByOutputFormat("STL"); // → FreeCAD + OpenSCAD presets
53
+ registry.getAllPremiumPresets(); // → 8 premium→guaranteed fallback chains
54
+ ```
55
+
56
+ ToolShopStudio now ships with a **live registry** — run `npm run toolshop registry list` or call `listTools()` from any MCP client.
57
+
58
+ ```bash
59
+ npm run toolshop registry list # table of all tools + preset counts
60
+ npm run toolshop registry show ffmpeg # full details for one tool
61
+ npm run toolshop registry summary # 5 tools, 27 presets, 15 formats
62
+ npm run toolshop registry json # registry summary as JSON
63
+ npm run toolshop registry docs # generate markdown docs to docs/
64
+ ```
65
+
44
66
  ## Quick Start
45
67
 
46
68
  ```bash
@@ -156,6 +178,23 @@ const model = await openscad.renderModel(
156
178
  - **Safe execution**: FreeCAD uses pre-baked Python one-liners (no exec/eval/user code)
157
179
  - **Multi-binary**: GDAL dispatches to gdalwarp, ogr2ogr, or gdal_translate per preset
158
180
  - **Text-first CAD**: OpenSCAD renders pure `.scad` text to mesh/image (no binary input)
181
+ - **Live Registry**: Validated singleton, MCP introspection tools, CLI, auto-generated docs
182
+
183
+ ## Generated Docs
184
+
185
+ The registry auto-generates markdown docs for every tool, all presets, and the full registry overview:
186
+
187
+ | Document | Contents |
188
+ |----------|----------|
189
+ | [`docs/registry.md`](docs/registry.md) | Full registry overview — all 5 tools, presets, patterns |
190
+ | [`docs/presets.md`](docs/presets.md) | Preset cross-reference — which presets exist on which tools |
191
+ | [`docs/tools/ffmpeg.md`](docs/tools/ffmpeg.md) | FFmpeg YouTube MCP — 7 presets, 14 patterns |
192
+ | [`docs/tools/pandoc.md`](docs/tools/pandoc.md) | Pandoc MCP — 5 presets, 11 patterns |
193
+ | [`docs/tools/freecad.md`](docs/tools/freecad.md) | FreeCAD MCP — 5 presets, 13 patterns |
194
+ | [`docs/tools/gdal.md`](docs/tools/gdal.md) | GDAL MCP — 5 presets, 14 patterns |
195
+ | [`docs/tools/openscad.md`](docs/tools/openscad.md) | OpenSCAD MCP — 5 presets, 15 patterns |
196
+
197
+ Regenerate at any time: `npm run docs:generate`
159
198
 
160
199
  ## Docker
161
200
 
@@ -171,9 +210,10 @@ All five runtime binaries (`ffmpeg`, `pandoc`, `freecad-cmd`, `gdal-bin`, `opens
171
210
  ```bash
172
211
  npm install # dependencies
173
212
  npm run typecheck # tsc --noEmit
174
- npm test # vitest (189 tests)
213
+ npm test # vitest (237 tests)
175
214
  npm run build # compile to dist/
176
- npm run smoke # end-to-end smoke (all five tools, 12 tests)
215
+ npm run smoke # end-to-end smoke (all five tools + registry, 13 tests)
216
+ npm run docs:generate # generate registry docs to docs/
177
217
  ```
178
218
 
179
219
  ## License
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "../dist/registry/cli.js";
3
+ main();
package/dist/index.d.ts CHANGED
@@ -13,3 +13,4 @@ export * as pandoc from "./pandoc/index.js";
13
13
  export * as freecad from "./freecad/index.js";
14
14
  export * as gdal from "./gdal/index.js";
15
15
  export * as openscad from "./openscad/index.js";
16
+ export * as registry from "./registry/index.js";
package/dist/index.js CHANGED
@@ -13,3 +13,4 @@ export * as pandoc from "./pandoc/index.js";
13
13
  export * as freecad from "./freecad/index.js";
14
14
  export * as gdal from "./gdal/index.js";
15
15
  export * as openscad from "./openscad/index.js";
16
+ export * as registry from "./registry/index.js";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Generate a markdown document for a single tool.
3
+ * Returns empty string if tool is not found.
4
+ *
5
+ * Output includes: name, description, preset table, patterns, example.
6
+ * Pure function — no I/O, no file writes.
7
+ */
8
+ export declare function generateToolMarkdown(toolId: string): string;
9
+ /**
10
+ * Generate a combined markdown document for all tools.
11
+ * Useful for full registry documentation.
12
+ */
13
+ export declare function generateRegistryMarkdown(): string;
14
+ /**
15
+ * Generate a preset cross-reference table across all tools.
16
+ * Shows which presets are shared (e.g. stl-print-ready in FreeCAD + OpenSCAD).
17
+ */
18
+ export declare function generatePresetCrossRef(): string;
19
+ /**
20
+ * Generate all tool docs and write them to disk.
21
+ *
22
+ * Creates:
23
+ * - `<outDir>/tools/<toolId>.md` for each tool
24
+ * - `<outDir>/registry.md` — combined registry doc
25
+ * - `<outDir>/presets.md` — preset cross-reference
26
+ *
27
+ * @param outDir Target directory (default: "docs")
28
+ * @returns Array of file paths written
29
+ */
30
+ export declare function generateAllToolDocs(outDir?: string): string[];
@@ -0,0 +1,130 @@
1
+ import { writeFileSync, mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { loadRegistry } from "./runtime.js";
4
+ // ── Auto-docs — pure markdown generation from registry data ──────
5
+ /**
6
+ * Generate a markdown document for a single tool.
7
+ * Returns empty string if tool is not found.
8
+ *
9
+ * Output includes: name, description, preset table, patterns, example.
10
+ * Pure function — no I/O, no file writes.
11
+ */
12
+ export function generateToolMarkdown(toolId) {
13
+ const reg = loadRegistry();
14
+ const tool = reg.get(toolId);
15
+ if (!tool)
16
+ return "";
17
+ const lines = [
18
+ `# ${tool.name}`,
19
+ "",
20
+ `> ${tool.description}`,
21
+ "",
22
+ `**Version:** ${tool.version} `,
23
+ `**Pipeline:** \`${tool.pipelineFn}\``,
24
+ "",
25
+ "## Presets",
26
+ "",
27
+ "| Preset | Format | Extension | Tier | Fallback |",
28
+ "|--------|--------|-----------|------|----------|",
29
+ ];
30
+ for (const [name, info] of Object.entries(tool.presets)) {
31
+ const tier = info.isPremium ? "Premium" : "Guaranteed";
32
+ const fallback = info.fallbackTo ?? "—";
33
+ lines.push(`| \`${name}\` | ${info.outputFormat} | \`.${info.outputExt}\` | ${tier} | ${fallback} |`);
34
+ }
35
+ lines.push("", "## Architectural Patterns", "");
36
+ for (const p of tool.commonPatterns) {
37
+ lines.push(`- ${p}`);
38
+ }
39
+ if (tool.examples.length > 0) {
40
+ lines.push("", "## Example", "");
41
+ const ex = tool.examples[0];
42
+ lines.push(`**${ex.description}**`, "", "```typescript", ex.code, "```");
43
+ }
44
+ return lines.join("\n");
45
+ }
46
+ /**
47
+ * Generate a combined markdown document for all tools.
48
+ * Useful for full registry documentation.
49
+ */
50
+ export function generateRegistryMarkdown() {
51
+ const reg = loadRegistry();
52
+ const s = reg.summary;
53
+ const header = [
54
+ "# ToolShopStudio Registry",
55
+ "",
56
+ `> ${s.toolCount} tools, ${s.totalPresets} presets (${s.guaranteedPresets} guaranteed, ${s.premiumPresets} premium)`,
57
+ "",
58
+ `**Version:** ${s.version} `,
59
+ `**Output Formats:** ${s.uniqueOutputFormats.join(", ")}`,
60
+ "",
61
+ "---",
62
+ "",
63
+ ];
64
+ const toolDocs = reg
65
+ .ids()
66
+ .map((id) => generateToolMarkdown(id))
67
+ .filter(Boolean);
68
+ return [...header, ...toolDocs.join("\n\n---\n\n").split("\n")].join("\n");
69
+ }
70
+ /**
71
+ * Generate a preset cross-reference table across all tools.
72
+ * Shows which presets are shared (e.g. stl-print-ready in FreeCAD + OpenSCAD).
73
+ */
74
+ export function generatePresetCrossRef() {
75
+ const reg = loadRegistry();
76
+ const presetMap = new Map();
77
+ for (const tool of reg.all()) {
78
+ for (const presetName of Object.keys(tool.presets)) {
79
+ const list = presetMap.get(presetName) ?? [];
80
+ list.push(tool.id);
81
+ presetMap.set(presetName, list);
82
+ }
83
+ }
84
+ const lines = [
85
+ "# Preset Cross-Reference",
86
+ "",
87
+ "| Preset | Tools |",
88
+ "|--------|-------|",
89
+ ];
90
+ for (const [preset, tools] of [...presetMap.entries()].sort()) {
91
+ lines.push(`| \`${preset}\` | ${tools.join(", ")} |`);
92
+ }
93
+ return lines.join("\n");
94
+ }
95
+ // ── File-writing build step ──────────────────────────────────────
96
+ /**
97
+ * Generate all tool docs and write them to disk.
98
+ *
99
+ * Creates:
100
+ * - `<outDir>/tools/<toolId>.md` for each tool
101
+ * - `<outDir>/registry.md` — combined registry doc
102
+ * - `<outDir>/presets.md` — preset cross-reference
103
+ *
104
+ * @param outDir Target directory (default: "docs")
105
+ * @returns Array of file paths written
106
+ */
107
+ export function generateAllToolDocs(outDir = "docs") {
108
+ const reg = loadRegistry();
109
+ const toolsDir = join(outDir, "tools");
110
+ mkdirSync(toolsDir, { recursive: true });
111
+ const written = [];
112
+ // Per-tool docs
113
+ for (const id of reg.ids()) {
114
+ const md = generateToolMarkdown(id);
115
+ if (md) {
116
+ const filePath = join(toolsDir, `${id}.md`);
117
+ writeFileSync(filePath, md + "\n", "utf8");
118
+ written.push(filePath);
119
+ }
120
+ }
121
+ // Combined registry doc
122
+ const registryPath = join(outDir, "registry.md");
123
+ writeFileSync(registryPath, generateRegistryMarkdown() + "\n", "utf8");
124
+ written.push(registryPath);
125
+ // Preset cross-reference
126
+ const presetsPath = join(outDir, "presets.md");
127
+ writeFileSync(presetsPath, generatePresetCrossRef() + "\n", "utf8");
128
+ written.push(presetsPath);
129
+ return written;
130
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Format the tool list as a printable table string.
3
+ */
4
+ export declare function formatToolList(): string;
5
+ /**
6
+ * Format a single tool's full info as a printable string.
7
+ */
8
+ export declare function formatToolShow(toolId: string): string;
9
+ /**
10
+ * Format the registry summary as a printable string.
11
+ */
12
+ export declare function formatSummary(): string;
13
+ /**
14
+ * Format the registry summary as JSON string.
15
+ */
16
+ export declare function formatJson(): string;
17
+ /**
18
+ * Run the docs generator and return a status message.
19
+ * @param outDir Target directory (default: "docs")
20
+ */
21
+ export declare function runDocsGenerate(outDir?: string): string;
22
+ /**
23
+ * Process CLI arguments and return the output string.
24
+ * Separated from I/O for testability.
25
+ */
26
+ export declare function processCommand(args: string[]): string;
27
+ /**
28
+ * CLI main — reads process.argv and writes to stdout.
29
+ * Called from bin/toolshop.mjs.
30
+ */
31
+ export declare function main(): void;
@@ -0,0 +1,129 @@
1
+ import { loadRegistry } from "./runtime.js";
2
+ import { generateAllToolDocs } from "./auto-docs.js";
3
+ // ── CLI registry commands — testable core logic ──────────────────
4
+ /**
5
+ * Format the tool list as a printable table string.
6
+ */
7
+ export function formatToolList() {
8
+ const reg = loadRegistry();
9
+ const tools = reg.all();
10
+ const header = "ID Presets Premium Version";
11
+ const separator = "─".repeat(header.length);
12
+ const rows = tools.map((t) => {
13
+ const presetCount = Object.keys(t.presets).length;
14
+ const premiumCount = Object.values(t.presets).filter((p) => p.isPremium).length;
15
+ return [
16
+ t.id.padEnd(16),
17
+ String(presetCount).padStart(7),
18
+ String(premiumCount).padStart(8),
19
+ ` ${t.version}`,
20
+ ].join("");
21
+ });
22
+ return [header, separator, ...rows].join("\n");
23
+ }
24
+ /**
25
+ * Format a single tool's full info as a printable string.
26
+ */
27
+ export function formatToolShow(toolId) {
28
+ const reg = loadRegistry();
29
+ const tool = reg.get(toolId);
30
+ if (!tool) {
31
+ return `Error: tool "${toolId}" not found. Available: ${reg.ids().join(", ")}`;
32
+ }
33
+ const lines = [
34
+ `${tool.name} (${tool.id}) v${tool.version}`,
35
+ "",
36
+ tool.description,
37
+ "",
38
+ "Presets:",
39
+ ];
40
+ for (const [name, info] of Object.entries(tool.presets)) {
41
+ const tier = info.isPremium ? `Premium → ${info.fallbackTo}` : "Guaranteed";
42
+ lines.push(` ${name.padEnd(24)} .${info.outputExt} [${tier}]`);
43
+ }
44
+ lines.push("", "Patterns:");
45
+ for (const p of tool.commonPatterns) {
46
+ lines.push(` • ${p}`);
47
+ }
48
+ return lines.join("\n");
49
+ }
50
+ /**
51
+ * Format the registry summary as a printable string.
52
+ */
53
+ export function formatSummary() {
54
+ const reg = loadRegistry();
55
+ const s = reg.summary;
56
+ return [
57
+ `ToolShopStudio Registry v${s.version}`,
58
+ "",
59
+ ` Tools: ${s.toolCount}`,
60
+ ` Presets: ${s.totalPresets} (${s.guaranteedPresets} guaranteed, ${s.premiumPresets} premium)`,
61
+ ` Formats: ${s.uniqueOutputFormats.join(", ")}`,
62
+ ].join("\n");
63
+ }
64
+ /**
65
+ * Format the registry summary as JSON string.
66
+ */
67
+ export function formatJson() {
68
+ const reg = loadRegistry();
69
+ return JSON.stringify(reg.summary, null, 2);
70
+ }
71
+ /**
72
+ * Run the docs generator and return a status message.
73
+ * @param outDir Target directory (default: "docs")
74
+ */
75
+ export function runDocsGenerate(outDir = "docs") {
76
+ const files = generateAllToolDocs(outDir);
77
+ return `Generated ${files.length} docs:\n${files.map((f) => ` ${f}`).join("\n")}`;
78
+ }
79
+ // ── CLI entry point ──────────────────────────────────────────────
80
+ /**
81
+ * Process CLI arguments and return the output string.
82
+ * Separated from I/O for testability.
83
+ */
84
+ export function processCommand(args) {
85
+ const sub = args[0];
86
+ switch (sub) {
87
+ case "list":
88
+ return formatToolList();
89
+ case "show": {
90
+ const toolId = args[1];
91
+ if (!toolId)
92
+ return "Usage: toolshop registry show <toolId>";
93
+ return formatToolShow(toolId);
94
+ }
95
+ case "summary":
96
+ return formatSummary();
97
+ case "json":
98
+ return formatJson();
99
+ case "docs": {
100
+ const outDir = args[1] || "docs";
101
+ return runDocsGenerate(outDir);
102
+ }
103
+ default:
104
+ return [
105
+ "Usage: toolshop registry <command>",
106
+ "",
107
+ "Commands:",
108
+ " list List all registered tools",
109
+ " show <id> Show full details for a tool",
110
+ " summary Show registry summary",
111
+ " json Output registry summary as JSON",
112
+ " docs [dir] Generate markdown docs (default: docs/)",
113
+ ].join("\n");
114
+ }
115
+ }
116
+ /**
117
+ * CLI main — reads process.argv and writes to stdout.
118
+ * Called from bin/toolshop.mjs.
119
+ */
120
+ export function main() {
121
+ const top = process.argv[2];
122
+ if (top === "registry") {
123
+ const output = processCommand(process.argv.slice(3));
124
+ console.log(output);
125
+ }
126
+ else {
127
+ console.log("Usage: toolshop registry <list|show|summary|json|docs>");
128
+ }
129
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Check that ALL tools implement a specific architectural pattern.
3
+ * Uses case-insensitive substring matching.
4
+ *
5
+ * Returns true if every registered tool has at least one
6
+ * commonPattern entry that contains the given string.
7
+ */
8
+ export declare function checkAllToolsHavePattern(pattern: string): boolean;
9
+ /**
10
+ * Get tools that are MISSING a specific architectural pattern.
11
+ * Useful for finding tools that need to be updated.
12
+ */
13
+ export declare function getToolsMissingPattern(pattern: string): string[];
14
+ /**
15
+ * Check that all tools share the same version string.
16
+ * Returns the version if consistent, throws if mismatched.
17
+ */
18
+ export declare function checkVersionConsistency(): string;
19
+ /**
20
+ * Get the version of a specific tool.
21
+ * Returns null if the tool is not registered.
22
+ */
23
+ export declare function getToolVersion(id: string): string | null;
24
+ /**
25
+ * Check that all shared patterns from SHARED_PATTERNS are present
26
+ * in every tool. Returns an array of { toolId, missingPattern } entries.
27
+ * Empty array means fully consistent.
28
+ */
29
+ export declare function checkSharedPatternCoverage(): Array<{
30
+ toolId: string;
31
+ missingPattern: string;
32
+ }>;
33
+ /**
34
+ * Get a summary of the registry: tool count, total presets,
35
+ * premium count, guaranteed count, unique output formats.
36
+ */
37
+ export declare function getRegistrySummary(): {
38
+ toolCount: number;
39
+ totalPresets: number;
40
+ premiumPresets: number;
41
+ guaranteedPresets: number;
42
+ uniqueOutputFormats: string[];
43
+ version: string;
44
+ };
@@ -0,0 +1,89 @@
1
+ import { TOOL_REGISTRY } from "./tools.js";
2
+ import { SHARED_PATTERNS } from "./helpers.js";
3
+ // ── Consistency checks — pure cross-tool assertions ──────────────
4
+ /**
5
+ * Check that ALL tools implement a specific architectural pattern.
6
+ * Uses case-insensitive substring matching.
7
+ *
8
+ * Returns true if every registered tool has at least one
9
+ * commonPattern entry that contains the given string.
10
+ */
11
+ export function checkAllToolsHavePattern(pattern) {
12
+ const lower = pattern.toLowerCase();
13
+ return Object.values(TOOL_REGISTRY).every((tool) => tool.commonPatterns.some((p) => p.toLowerCase().includes(lower)));
14
+ }
15
+ /**
16
+ * Get tools that are MISSING a specific architectural pattern.
17
+ * Useful for finding tools that need to be updated.
18
+ */
19
+ export function getToolsMissingPattern(pattern) {
20
+ const lower = pattern.toLowerCase();
21
+ return Object.entries(TOOL_REGISTRY)
22
+ .filter(([, tool]) => !tool.commonPatterns.some((p) => p.toLowerCase().includes(lower)))
23
+ .map(([id]) => id);
24
+ }
25
+ /**
26
+ * Check that all tools share the same version string.
27
+ * Returns the version if consistent, throws if mismatched.
28
+ */
29
+ export function checkVersionConsistency() {
30
+ const versions = new Set(Object.values(TOOL_REGISTRY).map((t) => t.version));
31
+ if (versions.size !== 1) {
32
+ const details = Object.entries(TOOL_REGISTRY)
33
+ .map(([id, t]) => ` ${id}: ${t.version}`)
34
+ .join("\n");
35
+ throw new Error(`Version mismatch across tools:\n${details}`);
36
+ }
37
+ return [...versions][0];
38
+ }
39
+ /**
40
+ * Get the version of a specific tool.
41
+ * Returns null if the tool is not registered.
42
+ */
43
+ export function getToolVersion(id) {
44
+ return TOOL_REGISTRY[id]?.version ?? null;
45
+ }
46
+ /**
47
+ * Check that all shared patterns from SHARED_PATTERNS are present
48
+ * in every tool. Returns an array of { toolId, missingPattern } entries.
49
+ * Empty array means fully consistent.
50
+ */
51
+ export function checkSharedPatternCoverage() {
52
+ const gaps = [];
53
+ for (const [toolId, tool] of Object.entries(TOOL_REGISTRY)) {
54
+ for (const required of SHARED_PATTERNS) {
55
+ const found = tool.commonPatterns.some((p) => p.toLowerCase().includes(required.toLowerCase()));
56
+ if (!found) {
57
+ gaps.push({ toolId, missingPattern: required });
58
+ }
59
+ }
60
+ }
61
+ return gaps;
62
+ }
63
+ /**
64
+ * Get a summary of the registry: tool count, total presets,
65
+ * premium count, guaranteed count, unique output formats.
66
+ */
67
+ export function getRegistrySummary() {
68
+ const tools = Object.values(TOOL_REGISTRY);
69
+ let totalPresets = 0;
70
+ let premiumPresets = 0;
71
+ const formats = new Set();
72
+ for (const tool of tools) {
73
+ for (const info of Object.values(tool.presets)) {
74
+ totalPresets++;
75
+ if (info.isPremium)
76
+ premiumPresets++;
77
+ if (info.outputFormat)
78
+ formats.add(info.outputFormat.toUpperCase());
79
+ }
80
+ }
81
+ return {
82
+ toolCount: tools.length,
83
+ totalPresets,
84
+ premiumPresets,
85
+ guaranteedPresets: totalPresets - premiumPresets,
86
+ uniqueOutputFormats: [...formats].sort(),
87
+ version: tools[0]?.version ?? "unknown",
88
+ };
89
+ }
@@ -0,0 +1 @@
1
+ export {};