@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.
- package/.github/workflows/ci.yml +8 -3
- package/CHANGELOG.md +25 -0
- package/Dockerfile +3 -3
- package/README.md +44 -4
- package/bin/toolshop.mjs +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/registry/auto-docs.d.ts +30 -0
- package/dist/registry/auto-docs.js +130 -0
- package/dist/registry/cli.d.ts +31 -0
- package/dist/registry/cli.js +129 -0
- package/dist/registry/consistency.d.ts +44 -0
- package/dist/registry/consistency.js +89 -0
- package/dist/registry/engine.test.d.ts +1 -0
- package/dist/registry/engine.test.js +109 -0
- package/dist/registry/full.test.d.ts +1 -0
- package/dist/registry/full.test.js +103 -0
- package/dist/registry/helpers.d.ts +20 -0
- package/dist/registry/helpers.js +39 -0
- package/dist/registry/index.d.ts +12 -0
- package/dist/registry/index.js +13 -0
- package/dist/registry/mcp-server.d.ts +61 -0
- package/dist/registry/mcp-server.js +40 -0
- package/dist/registry/mcp-tools.d.ts +85 -0
- package/dist/registry/mcp-tools.js +76 -0
- package/dist/registry/registry.test.d.ts +1 -0
- package/dist/registry/registry.test.js +57 -0
- package/dist/registry/resource.d.ts +126 -0
- package/dist/registry/resource.js +73 -0
- package/dist/registry/runtime.d.ts +72 -0
- package/dist/registry/runtime.js +43 -0
- package/dist/registry/runtime.test.d.ts +1 -0
- package/dist/registry/runtime.test.js +129 -0
- package/dist/registry/search.d.ts +77 -0
- package/dist/registry/search.js +136 -0
- package/dist/registry/tools.d.ts +8 -0
- package/dist/registry/tools.js +358 -0
- package/dist/registry/types.d.ts +400 -0
- package/dist/registry/types.js +74 -0
- package/dist/registry/validate.d.ts +27 -0
- package/dist/registry/validate.js +86 -0
- package/docs/presets.md +29 -0
- package/docs/registry.md +240 -0
- package/docs/tools/ffmpeg.md +46 -0
- package/docs/tools/freecad.md +43 -0
- package/docs/tools/gdal.md +44 -0
- package/docs/tools/openscad.md +45 -0
- package/docs/tools/pandoc.md +41 -0
- package/package.json +9 -4
- package/scripts/release.mjs +1 -1
- package/smoke.mjs +24 -1
- package/src/index.ts +1 -0
- package/src/registry/auto-docs.ts +155 -0
- package/src/registry/cli.ts +146 -0
- package/src/registry/consistency.ts +117 -0
- package/src/registry/engine.test.ts +137 -0
- package/src/registry/full.test.ts +120 -0
- package/src/registry/helpers.ts +55 -0
- package/src/registry/index.ts +13 -0
- package/src/registry/mcp-server.ts +49 -0
- package/src/registry/mcp-tools.ts +117 -0
- package/src/registry/registry.test.ts +75 -0
- package/src/registry/resource.ts +94 -0
- package/src/registry/runtime.test.ts +154 -0
- package/src/registry/runtime.ts +115 -0
- package/src/registry/search.ts +179 -0
- package/src/registry/tools.ts +374 -0
- package/src/registry/types.ts +91 -0
- package/src/registry/validate.ts +109 -0
package/.github/workflows/ci.yml
CHANGED
|
@@ -23,7 +23,7 @@ concurrency:
|
|
|
23
23
|
|
|
24
24
|
jobs:
|
|
25
25
|
test:
|
|
26
|
-
name: Typecheck + Test +
|
|
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 (
|
|
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:
|
|
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="
|
|
51
|
-
org.opencontainers.image.version="1.
|
|
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
|
-
|
|
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-
|
|
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 (
|
|
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,
|
|
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
|
package/bin/toolshop.mjs
ADDED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -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 {};
|