jui-tools-mcp-server 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/test.yml +35 -0
- package/README.md +148 -0
- package/data/attribute_definitions.json +4248 -0
- package/data/attribute_semantics.json +137 -0
- package/data/binding_semantics.json +115 -0
- package/data/component_metadata.json +769 -0
- package/data/coverage.json +114 -0
- package/data/screen_identity.json +350 -0
- package/dist/cli_runner.d.ts +10 -0
- package/dist/cli_runner.js +33 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +67 -0
- package/dist/data/component_metadata.d.ts +30 -0
- package/dist/data/component_metadata.js +498 -0
- package/dist/data/derived.d.ts +198 -0
- package/dist/data/derived.js +293 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +130 -0
- package/dist/spec_loader.d.ts +110 -0
- package/dist/spec_loader.js +445 -0
- package/dist/tools/api/list_api_models.d.ts +10 -0
- package/dist/tools/api/list_api_models.js +37 -0
- package/dist/tools/api/list_api_specs.d.ts +19 -0
- package/dist/tools/api/list_api_specs.js +42 -0
- package/dist/tools/api/preview_api_model_sync.d.ts +11 -0
- package/dist/tools/api/preview_api_model_sync.js +34 -0
- package/dist/tools/context/get_project_config.d.ts +3 -0
- package/dist/tools/context/get_project_config.js +20 -0
- package/dist/tools/context/list_component_specs.d.ts +3 -0
- package/dist/tools/context/list_component_specs.js +35 -0
- package/dist/tools/context/list_layouts.d.ts +3 -0
- package/dist/tools/context/list_layouts.js +118 -0
- package/dist/tools/context/list_screen_specs.d.ts +3 -0
- package/dist/tools/context/list_screen_specs.js +37 -0
- package/dist/tools/context/read_layout_file.d.ts +3 -0
- package/dist/tools/context/read_layout_file.js +25 -0
- package/dist/tools/context/read_spec_file.d.ts +3 -0
- package/dist/tools/context/read_spec_file.js +27 -0
- package/dist/tools/doc/doc_generate_component.d.ts +3 -0
- package/dist/tools/doc/doc_generate_component.js +38 -0
- package/dist/tools/doc/doc_generate_html.d.ts +3 -0
- package/dist/tools/doc/doc_generate_html.js +46 -0
- package/dist/tools/doc/doc_generate_spec.d.ts +3 -0
- package/dist/tools/doc/doc_generate_spec.js +38 -0
- package/dist/tools/doc/doc_init_component.d.ts +3 -0
- package/dist/tools/doc/doc_init_component.js +30 -0
- package/dist/tools/doc/doc_init_spec.d.ts +3 -0
- package/dist/tools/doc/doc_init_spec.js +33 -0
- package/dist/tools/doc/doc_rules_init.d.ts +3 -0
- package/dist/tools/doc/doc_rules_init.js +25 -0
- package/dist/tools/doc/doc_rules_show.d.ts +3 -0
- package/dist/tools/doc/doc_rules_show.js +21 -0
- package/dist/tools/doc/doc_validate_component.d.ts +3 -0
- package/dist/tools/doc/doc_validate_component.js +39 -0
- package/dist/tools/doc/doc_validate_spec.d.ts +3 -0
- package/dist/tools/doc/doc_validate_spec.js +40 -0
- package/dist/tools/jui/jui_build.d.ts +3 -0
- package/dist/tools/jui/jui_build.js +31 -0
- package/dist/tools/jui/jui_generate_converter.d.ts +3 -0
- package/dist/tools/jui/jui_generate_converter.js +43 -0
- package/dist/tools/jui/jui_generate_project.d.ts +3 -0
- package/dist/tools/jui/jui_generate_project.js +43 -0
- package/dist/tools/jui/jui_generate_screen.d.ts +3 -0
- package/dist/tools/jui/jui_generate_screen.js +22 -0
- package/dist/tools/jui/jui_init.d.ts +3 -0
- package/dist/tools/jui/jui_init.js +42 -0
- package/dist/tools/jui/jui_migrate_layouts.d.ts +3 -0
- package/dist/tools/jui/jui_migrate_layouts.js +25 -0
- package/dist/tools/jui/jui_sync_tool.d.ts +3 -0
- package/dist/tools/jui/jui_sync_tool.js +33 -0
- package/dist/tools/jui/jui_verify.d.ts +3 -0
- package/dist/tools/jui/jui_verify.js +43 -0
- package/dist/tools/spec/get_binding_rules.d.ts +3 -0
- package/dist/tools/spec/get_binding_rules.js +8 -0
- package/dist/tools/spec/get_data_source.d.ts +3 -0
- package/dist/tools/spec/get_data_source.js +41 -0
- package/dist/tools/spec/get_modifier_order.d.ts +3 -0
- package/dist/tools/spec/get_modifier_order.js +14 -0
- package/dist/tools/spec/get_platform_mapping.d.ts +3 -0
- package/dist/tools/spec/get_platform_mapping.js +20 -0
- package/dist/tools/spec/get_screen_identity.d.ts +3 -0
- package/dist/tools/spec/get_screen_identity.js +22 -0
- package/dist/tools/spec/lookup_attribute.d.ts +3 -0
- package/dist/tools/spec/lookup_attribute.js +14 -0
- package/dist/tools/spec/lookup_component.d.ts +3 -0
- package/dist/tools/spec/lookup_component.js +15 -0
- package/dist/tools/spec/search_components.d.ts +3 -0
- package/dist/tools/spec/search_components.js +14 -0
- package/dist/tools/test/test_artifacts_pull.d.ts +3 -0
- package/dist/tools/test/test_artifacts_pull.js +48 -0
- package/dist/tools/test/test_artifacts_status.d.ts +3 -0
- package/dist/tools/test/test_artifacts_status.js +28 -0
- package/dist/tools/test/test_generate_description.d.ts +3 -0
- package/dist/tools/test/test_generate_description.js +27 -0
- package/dist/tools/test/test_generate_flow.d.ts +3 -0
- package/dist/tools/test/test_generate_flow.js +29 -0
- package/dist/tools/test/test_generate_screen.d.ts +3 -0
- package/dist/tools/test/test_generate_screen.js +29 -0
- package/dist/tools/test/test_mock_generate.d.ts +3 -0
- package/dist/tools/test/test_mock_generate.js +64 -0
- package/dist/tools/test/test_report.d.ts +3 -0
- package/dist/tools/test/test_report.js +26 -0
- package/dist/tools/test/test_validate.d.ts +3 -0
- package/dist/tools/test/test_validate.js +43 -0
- package/docs/design.md +227 -0
- package/install.sh +90 -0
- package/package.json +24 -0
- package/scripts/fetch-definitions.js +101 -0
- package/scripts/fetch-manifest.js +60 -0
- package/src/cli_runner.ts +55 -0
- package/src/config.ts +88 -0
- package/src/data/derived.ts +305 -0
- package/src/index.ts +157 -0
- package/src/spec_loader.ts +591 -0
- package/src/tools/api/list_api_models.ts +44 -0
- package/src/tools/api/list_api_specs.ts +53 -0
- package/src/tools/api/preview_api_model_sync.ts +41 -0
- package/src/tools/context/get_project_config.ts +27 -0
- package/src/tools/context/list_component_specs.ts +44 -0
- package/src/tools/context/list_layouts.ts +131 -0
- package/src/tools/context/list_screen_specs.ts +46 -0
- package/src/tools/context/read_layout_file.ts +35 -0
- package/src/tools/context/read_spec_file.ts +38 -0
- package/src/tools/doc/doc_generate_component.ts +43 -0
- package/src/tools/doc/doc_generate_html.ts +40 -0
- package/src/tools/doc/doc_generate_spec.ts +43 -0
- package/src/tools/doc/doc_init_component.ts +32 -0
- package/src/tools/doc/doc_init_spec.ts +37 -0
- package/src/tools/doc/doc_rules_init.ts +29 -0
- package/src/tools/doc/doc_rules_show.ts +27 -0
- package/src/tools/doc/doc_validate_component.ts +47 -0
- package/src/tools/doc/doc_validate_spec.ts +48 -0
- package/src/tools/jui/jui_build.ts +31 -0
- package/src/tools/jui/jui_generate_converter.ts +41 -0
- package/src/tools/jui/jui_generate_project.ts +37 -0
- package/src/tools/jui/jui_generate_screen.ts +28 -0
- package/src/tools/jui/jui_init.ts +38 -0
- package/src/tools/jui/jui_migrate_layouts.ts +29 -0
- package/src/tools/jui/jui_sync_tool.ts +33 -0
- package/src/tools/jui/jui_verify.ts +43 -0
- package/src/tools/spec/get_binding_rules.ts +16 -0
- package/src/tools/spec/get_data_source.ts +53 -0
- package/src/tools/spec/get_modifier_order.ts +22 -0
- package/src/tools/spec/get_platform_mapping.ts +28 -0
- package/src/tools/spec/get_screen_identity.ts +31 -0
- package/src/tools/spec/lookup_attribute.ts +22 -0
- package/src/tools/spec/lookup_component.ts +23 -0
- package/src/tools/spec/search_components.ts +22 -0
- package/src/tools/test/test_artifacts_pull.ts +47 -0
- package/src/tools/test/test_artifacts_status.ts +34 -0
- package/src/tools/test/test_generate_description.ts +33 -0
- package/src/tools/test/test_generate_flow.ts +33 -0
- package/src/tools/test/test_generate_screen.ts +33 -0
- package/src/tools/test/test_mock_generate.ts +58 -0
- package/src/tools/test/test_report.ts +32 -0
- package/src/tools/test/test_validate.ts +43 -0
- package/tests/__snapshots__/derived.test.ts.snap +279 -0
- package/tests/bundled_snapshot.test.ts +155 -0
- package/tests/cli_runner.test.ts +120 -0
- package/tests/config.test.ts +212 -0
- package/tests/derived.test.ts +114 -0
- package/tests/helpers.ts +272 -0
- package/tests/setup.ts +27 -0
- package/tests/snapshot_completeness.test.ts +152 -0
- package/tests/spec_loader.test.ts +488 -0
- package/tests/tools_cli.test.ts +687 -0
- package/tests/tools_context.test.ts +274 -0
- package/tests/tools_spec.test.ts +225 -0
- package/tests/tools_test.test.ts +284 -0
- package/tsconfig.json +15 -0
- package/uninstall.sh +46 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli } from "../../cli_runner.js";
|
|
3
|
+
/**
|
|
4
|
+
* Discovery wrapper around `jui g api --dry-run --json`.
|
|
5
|
+
*
|
|
6
|
+
* Returns the schema filter result + the file list that *would* be written
|
|
7
|
+
* for each platform, without touching disk. Agents call this after a
|
|
8
|
+
* filter config change to confirm which schemas survive before running
|
|
9
|
+
* `jui build` (which would actually emit + orphan-prune).
|
|
10
|
+
*/
|
|
11
|
+
export function register(server, config) {
|
|
12
|
+
server.tool("preview_api_model_sync", "Preview swagger → DTO + Domain generation without writing files. Returns kept_schemas / filtered_out / skip_domain_matches and the per-platform file list that would be emitted.", {
|
|
13
|
+
platform: z.enum(["ios", "android", "web"]).optional()
|
|
14
|
+
.describe("Restrict to a single platform"),
|
|
15
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
16
|
+
}, async ({ platform, project_dir }) => {
|
|
17
|
+
try {
|
|
18
|
+
const projectDir = config.resolveProjectDir(project_dir);
|
|
19
|
+
const args = ["g", "api", "--dry-run", "--json"];
|
|
20
|
+
if (platform) {
|
|
21
|
+
args.push("--platform", platform);
|
|
22
|
+
}
|
|
23
|
+
const result = await runCli("jui", args, { cwd: projectDir });
|
|
24
|
+
// Non-zero exit may indicate a swagger halt (oneOf, multi-file ref,
|
|
25
|
+
// etc.). The CLI still emits structured JSON on the error path, so
|
|
26
|
+
// pass stdout through when present.
|
|
27
|
+
const body = result.stdout.trim() || result.stderr.trim();
|
|
28
|
+
return { content: [{ type: "text", text: body || "preview returned no output" }] };
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function register(server, config) {
|
|
3
|
+
server.tool("get_project_config", "Read jui.config.json and return project configuration (platforms, directories, project name)", {
|
|
4
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
5
|
+
}, async ({ project_dir }) => {
|
|
6
|
+
try {
|
|
7
|
+
const projectDir = config.resolveProjectDir(project_dir);
|
|
8
|
+
const projectConfig = config.readProjectConfig(projectDir);
|
|
9
|
+
return {
|
|
10
|
+
content: [{
|
|
11
|
+
type: "text",
|
|
12
|
+
text: JSON.stringify({ project_dir: projectDir, config: projectConfig }, null, 2),
|
|
13
|
+
}],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readFileSync, readdirSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
export function register(server, config) {
|
|
5
|
+
server.tool("list_component_specs", "List all component spec files (*.component.json) with metadata", {
|
|
6
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
7
|
+
}, async ({ project_dir }) => {
|
|
8
|
+
try {
|
|
9
|
+
const projectDir = config.resolveProjectDir(project_dir);
|
|
10
|
+
const projectConfig = config.readProjectConfig(projectDir);
|
|
11
|
+
const compDir = config.resolveDir(projectConfig, "component_spec_directory", projectDir);
|
|
12
|
+
if (!existsSync(compDir)) {
|
|
13
|
+
return { content: [{ type: "text", text: `Component spec directory not found: ${compDir}` }] };
|
|
14
|
+
}
|
|
15
|
+
const files = readdirSync(compDir).filter((f) => f.endsWith(".component.json"));
|
|
16
|
+
const specs = files.map((file) => {
|
|
17
|
+
try {
|
|
18
|
+
const data = JSON.parse(readFileSync(join(compDir, file), "utf-8"));
|
|
19
|
+
return {
|
|
20
|
+
file,
|
|
21
|
+
name: data.metadata?.name || file.replace(".component.json", ""),
|
|
22
|
+
category: data.metadata?.category || "",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { file, name: file.replace(".component.json", ""), category: "unknown" };
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return { content: [{ type: "text", text: JSON.stringify(specs, null, 2) }] };
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readdirSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { runCli } from "../../cli_runner.js";
|
|
5
|
+
/**
|
|
6
|
+
* Screen classification for the listed layouts, obtained from `jui screens
|
|
7
|
+
* --json` rather than reimplemented here.
|
|
8
|
+
*
|
|
9
|
+
* Classification depends on how OTHER layouts reference this one, and the
|
|
10
|
+
* rules live in shared/core/screen_identity.json with one Python reader and
|
|
11
|
+
* one Ruby reader already held to each other by a cross-language test. A
|
|
12
|
+
* third reader in TypeScript would be a third thing to keep in sync, so this
|
|
13
|
+
* asks the CLI. When the CLI is unavailable or predates `jui screens`, the
|
|
14
|
+
* listing simply comes back without classification instead of guessing.
|
|
15
|
+
*/
|
|
16
|
+
async function classifyScreens(projectDir) {
|
|
17
|
+
const result = await runCli("jui", ["screens", "--json"], { cwd: projectDir, timeout: 30_000 });
|
|
18
|
+
if (!result.stdout.trim())
|
|
19
|
+
return null;
|
|
20
|
+
try {
|
|
21
|
+
const payload = JSON.parse(result.stdout);
|
|
22
|
+
const byId = new Map();
|
|
23
|
+
for (const entry of payload.entries ?? []) {
|
|
24
|
+
byId.set(entry.screen, {
|
|
25
|
+
role: entry.role,
|
|
26
|
+
reason: entry.reason,
|
|
27
|
+
marker: entry.marker ?? null,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return byId;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function register(server, config) {
|
|
37
|
+
server.tool("list_layouts", "List all Layout JSON files in the shared layouts directory", {
|
|
38
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
39
|
+
}, async ({ project_dir }) => {
|
|
40
|
+
try {
|
|
41
|
+
const projectDir = config.resolveProjectDir(project_dir);
|
|
42
|
+
const projectConfig = config.readProjectConfig(projectDir);
|
|
43
|
+
const layoutsDir = config.resolveDir(projectConfig, "layouts_directory", projectDir);
|
|
44
|
+
if (!existsSync(layoutsDir)) {
|
|
45
|
+
return { content: [{ type: "text", text: `Layouts directory not found: ${layoutsDir}` }] };
|
|
46
|
+
}
|
|
47
|
+
const collectJsonFiles = (dir, prefix = "") => {
|
|
48
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
49
|
+
const files = [];
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
52
|
+
if (entry.isDirectory()) {
|
|
53
|
+
files.push(...collectJsonFiles(join(dir, entry.name), relPath));
|
|
54
|
+
}
|
|
55
|
+
else if (entry.name.endsWith(".json")) {
|
|
56
|
+
files.push(relPath);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return files;
|
|
60
|
+
};
|
|
61
|
+
const files = collectJsonFiles(layoutsDir);
|
|
62
|
+
// Responsive variant files (home@regular.json) are attached to
|
|
63
|
+
// their base screen instead of listed as standalone layouts —
|
|
64
|
+
// they replace the base's whole tree per size-class tier and
|
|
65
|
+
// never have their own spec/VM/Data.
|
|
66
|
+
const isVariant = (relPath) => {
|
|
67
|
+
const basename = relPath.split("/").pop() ?? relPath;
|
|
68
|
+
return basename.includes("@");
|
|
69
|
+
};
|
|
70
|
+
const baseOf = (relPath) => {
|
|
71
|
+
const idx = relPath.lastIndexOf("@");
|
|
72
|
+
return idx > 0 ? `${relPath.slice(0, idx)}.json` : relPath;
|
|
73
|
+
};
|
|
74
|
+
const bases = files.filter((f) => !isVariant(f));
|
|
75
|
+
const variantsByBase = new Map();
|
|
76
|
+
for (const f of files) {
|
|
77
|
+
if (!isVariant(f))
|
|
78
|
+
continue;
|
|
79
|
+
const base = baseOf(f.replace(/\.json$/, ""));
|
|
80
|
+
const list = variantsByBase.get(base) ?? [];
|
|
81
|
+
list.push(f);
|
|
82
|
+
variantsByBase.set(base, list);
|
|
83
|
+
}
|
|
84
|
+
// Screen id = layout basename (variant-normalized); the role tells
|
|
85
|
+
// callers which layouts may legitimately appear as a test's `screen`
|
|
86
|
+
// and which are cells/partials that render inside a host.
|
|
87
|
+
const classification = await classifyScreens(projectDir);
|
|
88
|
+
const screenIdOf = (relPath) => {
|
|
89
|
+
const basename = relPath.split("/").pop() ?? relPath;
|
|
90
|
+
return basename.replace(/\.json$/, "").split("@")[0];
|
|
91
|
+
};
|
|
92
|
+
const listing = bases.map((f) => {
|
|
93
|
+
const variants = variantsByBase.get(f);
|
|
94
|
+
const info = classification?.get(screenIdOf(f));
|
|
95
|
+
if (!info && !variants)
|
|
96
|
+
return f;
|
|
97
|
+
return {
|
|
98
|
+
layout: f,
|
|
99
|
+
...(variants ? { variants: variants.sort() } : {}),
|
|
100
|
+
...(info
|
|
101
|
+
? { screen: screenIdOf(f), role: info.role, roleReason: info.reason }
|
|
102
|
+
: {}),
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
// Orphan variants (base missing) still surface — flagged so the
|
|
106
|
+
// caller can report them instead of silently dropping files.
|
|
107
|
+
for (const [base, variants] of variantsByBase) {
|
|
108
|
+
if (!bases.includes(base)) {
|
|
109
|
+
listing.push({ layout: base, missingBase: true, variants: variants.sort() });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { content: [{ type: "text", text: JSON.stringify(listing, null, 2) }] };
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readFileSync, readdirSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
export function register(server, config) {
|
|
5
|
+
server.tool("list_screen_specs", "List all screen spec files (*.spec.json) with metadata (name, displayName, type)", {
|
|
6
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
7
|
+
}, async ({ project_dir }) => {
|
|
8
|
+
try {
|
|
9
|
+
const projectDir = config.resolveProjectDir(project_dir);
|
|
10
|
+
const projectConfig = config.readProjectConfig(projectDir);
|
|
11
|
+
const specDir = config.resolveDir(projectConfig, "spec_directory", projectDir);
|
|
12
|
+
if (!existsSync(specDir)) {
|
|
13
|
+
return { content: [{ type: "text", text: `Spec directory not found: ${specDir}` }] };
|
|
14
|
+
}
|
|
15
|
+
const files = readdirSync(specDir).filter((f) => f.endsWith(".spec.json"));
|
|
16
|
+
const specs = files.map((file) => {
|
|
17
|
+
try {
|
|
18
|
+
const data = JSON.parse(readFileSync(join(specDir, file), "utf-8"));
|
|
19
|
+
return {
|
|
20
|
+
file,
|
|
21
|
+
name: data.metadata?.name || file.replace(".spec.json", ""),
|
|
22
|
+
displayName: data.metadata?.displayName || "",
|
|
23
|
+
type: data.type || "screen_spec",
|
|
24
|
+
layoutFile: data.metadata?.layoutFile || null,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return { file, name: file.replace(".spec.json", ""), displayName: "", type: "unknown", layoutFile: null };
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return { content: [{ type: "text", text: JSON.stringify(specs, null, 2) }] };
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readFileSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
export function register(server, config) {
|
|
5
|
+
server.tool("read_layout_file", "Read the contents of a Layout JSON file from the shared layouts directory", {
|
|
6
|
+
file: z.string().describe("Filename (e.g., 'Login.json' or 'Styles/Common.json')"),
|
|
7
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
8
|
+
}, async ({ file, project_dir }) => {
|
|
9
|
+
try {
|
|
10
|
+
const projectDir = config.resolveProjectDir(project_dir);
|
|
11
|
+
const projectConfig = config.readProjectConfig(projectDir);
|
|
12
|
+
const layoutsDir = config.resolveDir(projectConfig, "layouts_directory", projectDir);
|
|
13
|
+
const filePath = join(layoutsDir, file);
|
|
14
|
+
config.validatePathInProject(filePath, projectDir);
|
|
15
|
+
if (!existsSync(filePath)) {
|
|
16
|
+
return { content: [{ type: "text", text: `File not found: ${filePath}` }] };
|
|
17
|
+
}
|
|
18
|
+
const content = readFileSync(filePath, "utf-8");
|
|
19
|
+
return { content: [{ type: "text", text: content }] };
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readFileSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
export function register(server, config) {
|
|
5
|
+
server.tool("read_spec_file", "Read the contents of a screen or component spec file", {
|
|
6
|
+
file: z.string().describe("Filename (e.g., 'login.spec.json' or 'my_card.component.json')"),
|
|
7
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
8
|
+
}, async ({ file, project_dir }) => {
|
|
9
|
+
try {
|
|
10
|
+
const projectDir = config.resolveProjectDir(project_dir);
|
|
11
|
+
const projectConfig = config.readProjectConfig(projectDir);
|
|
12
|
+
const isComponent = file.endsWith(".component.json");
|
|
13
|
+
const dirField = isComponent ? "component_spec_directory" : "spec_directory";
|
|
14
|
+
const baseDir = config.resolveDir(projectConfig, dirField, projectDir);
|
|
15
|
+
const filePath = join(baseDir, file);
|
|
16
|
+
config.validatePathInProject(filePath, projectDir);
|
|
17
|
+
if (!existsSync(filePath)) {
|
|
18
|
+
return { content: [{ type: "text", text: `File not found: ${filePath}` }] };
|
|
19
|
+
}
|
|
20
|
+
const content = readFileSync(filePath, "utf-8");
|
|
21
|
+
return { content: [{ type: "text", text: content }] };
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
4
|
+
export function register(server, config) {
|
|
5
|
+
server.tool("doc_generate_component", "Generate HTML or Markdown documentation from a component spec file (or batch from a directory)", {
|
|
6
|
+
file: z.string().describe("Component spec file or directory path"),
|
|
7
|
+
output: z.string().optional().describe("Output file or directory path"),
|
|
8
|
+
format: z.enum(["html", "markdown"]).optional().describe("Output format (default: html)"),
|
|
9
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
13
|
+
let filePath = params.file;
|
|
14
|
+
if (!filePath.startsWith("/") && !filePath.includes("/")) {
|
|
15
|
+
try {
|
|
16
|
+
const projectConfig = config.readProjectConfig(projectDir);
|
|
17
|
+
const compDir = config.resolveDir(projectConfig, "component_spec_directory", projectDir);
|
|
18
|
+
filePath = join(compDir, filePath);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
filePath = join(projectDir, filePath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const args = ["generate", "component", filePath];
|
|
25
|
+
if (params.output) {
|
|
26
|
+
args.push("-o", params.output);
|
|
27
|
+
}
|
|
28
|
+
if (params.format) {
|
|
29
|
+
args.push("--format", params.format);
|
|
30
|
+
}
|
|
31
|
+
const result = await runCli("jsonui-doc", args, { cwd: projectDir });
|
|
32
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("doc_generate_html", "Generate HTML documentation site with index from a directory of test files. Supports multi-app sites (apps) and extra markdown doc directories (docs_dirs), same as the jsonui-doc CLI", {
|
|
5
|
+
input_dir: z.string().describe("Directory containing .test.json files"),
|
|
6
|
+
output_dir: z.string().optional().describe("Output directory for HTML files"),
|
|
7
|
+
title: z.string().optional().describe("Documentation title"),
|
|
8
|
+
apps: z.array(z.string()).optional().describe("App sections as NAME:DIR entries (repeatable --app); for multi-app whole-site generation"),
|
|
9
|
+
docs_dirs: z.array(z.string()).optional().describe("Additional markdown doc directories (repeatable -d/--docs)"),
|
|
10
|
+
figma_dir: z.string().optional().describe("Figma assets directory (-fig)"),
|
|
11
|
+
layouts_dir: z.string().optional().describe("Layouts directory override (--layouts-dir)"),
|
|
12
|
+
with_checks: z.boolean().optional().describe("Run doc contract checks during generation (--with-checks)"),
|
|
13
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
14
|
+
}, async (params) => {
|
|
15
|
+
try {
|
|
16
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
17
|
+
const args = ["generate", "html", params.input_dir];
|
|
18
|
+
if (params.output_dir) {
|
|
19
|
+
args.push("-o", params.output_dir);
|
|
20
|
+
}
|
|
21
|
+
if (params.title) {
|
|
22
|
+
args.push("-t", params.title);
|
|
23
|
+
}
|
|
24
|
+
for (const app of params.apps ?? []) {
|
|
25
|
+
args.push("--app", app);
|
|
26
|
+
}
|
|
27
|
+
for (const dir of params.docs_dirs ?? []) {
|
|
28
|
+
args.push("-d", dir);
|
|
29
|
+
}
|
|
30
|
+
if (params.figma_dir) {
|
|
31
|
+
args.push("-fig", params.figma_dir);
|
|
32
|
+
}
|
|
33
|
+
if (params.layouts_dir) {
|
|
34
|
+
args.push("--layouts-dir", params.layouts_dir);
|
|
35
|
+
}
|
|
36
|
+
if (params.with_checks) {
|
|
37
|
+
args.push("--with-checks");
|
|
38
|
+
}
|
|
39
|
+
const result = await runCli("jsonui-doc", args, { cwd: projectDir });
|
|
40
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
4
|
+
export function register(server, config) {
|
|
5
|
+
server.tool("doc_generate_spec", "Generate HTML or Markdown documentation from a screen spec file (or batch from a directory)", {
|
|
6
|
+
file: z.string().describe("Spec file or directory path (e.g., 'login.spec.json' or 'docs/screens/json')"),
|
|
7
|
+
output: z.string().optional().describe("Output file or directory path"),
|
|
8
|
+
format: z.enum(["html", "markdown"]).optional().describe("Output format (default: html)"),
|
|
9
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
13
|
+
let filePath = params.file;
|
|
14
|
+
if (!filePath.startsWith("/") && !filePath.includes("/")) {
|
|
15
|
+
try {
|
|
16
|
+
const projectConfig = config.readProjectConfig(projectDir);
|
|
17
|
+
const specDir = config.resolveDir(projectConfig, "spec_directory", projectDir);
|
|
18
|
+
filePath = join(specDir, filePath);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
filePath = join(projectDir, filePath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const args = ["generate", "spec", filePath];
|
|
25
|
+
if (params.output) {
|
|
26
|
+
args.push("-o", params.output);
|
|
27
|
+
}
|
|
28
|
+
if (params.format) {
|
|
29
|
+
args.push("--format", params.format);
|
|
30
|
+
}
|
|
31
|
+
const result = await runCli("jsonui-doc", args, { cwd: projectDir });
|
|
32
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("doc_init_component", "Create a new component specification template file (.component.json)", {
|
|
5
|
+
name: z.string().describe("Component name in PascalCase (e.g., 'CustomCard')"),
|
|
6
|
+
display_name: z.string().optional().describe("Display name for the component"),
|
|
7
|
+
category: z.enum(["card", "form", "list", "navigation", "input", "display", "layout", "feedback", "other"]).optional().describe("Component category"),
|
|
8
|
+
output_dir: z.string().optional().describe("Output directory"),
|
|
9
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
13
|
+
const args = ["init", "component", params.name];
|
|
14
|
+
if (params.display_name) {
|
|
15
|
+
args.push("-d", params.display_name);
|
|
16
|
+
}
|
|
17
|
+
if (params.category) {
|
|
18
|
+
args.push("-c", params.category);
|
|
19
|
+
}
|
|
20
|
+
if (params.output_dir) {
|
|
21
|
+
args.push("-o", params.output_dir);
|
|
22
|
+
}
|
|
23
|
+
const result = await runCli("jsonui-doc", args, { cwd: projectDir });
|
|
24
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("doc_init_spec", "Create a new screen specification template file (.spec.json)", {
|
|
5
|
+
name: z.string().describe("Screen name in PascalCase (e.g., 'LoginScreen')"),
|
|
6
|
+
display_name: z.string().optional().describe("Display name for the screen"),
|
|
7
|
+
output_dir: z.string().optional().describe("Output directory (defaults to spec_directory in config)"),
|
|
8
|
+
file_path: z.string().optional().describe("Explicit relative file path under output_dir " +
|
|
9
|
+
"(e.g., 'learn/hello-world.spec.json'). When omitted, the file name " +
|
|
10
|
+
"is derived from the PascalCase name via kebab-case " +
|
|
11
|
+
"(LearnHelloWorld -> learn-hello-world.spec.json)."),
|
|
12
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
13
|
+
}, async (params) => {
|
|
14
|
+
try {
|
|
15
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
16
|
+
const args = ["init", "spec", params.name];
|
|
17
|
+
if (params.display_name) {
|
|
18
|
+
args.push("-d", params.display_name);
|
|
19
|
+
}
|
|
20
|
+
if (params.output_dir) {
|
|
21
|
+
args.push("-o", params.output_dir);
|
|
22
|
+
}
|
|
23
|
+
if (params.file_path) {
|
|
24
|
+
args.push("-f", params.file_path);
|
|
25
|
+
}
|
|
26
|
+
const result = await runCli("jsonui-doc", args, { cwd: projectDir });
|
|
27
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("doc_rules_init", "Create a template .jsonui-doc-rules.json configuration file for custom validation rules", {
|
|
5
|
+
flutter: z.boolean().optional().describe("Include Flutter-specific rules"),
|
|
6
|
+
output_dir: z.string().optional().describe("Output directory for rules file"),
|
|
7
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
8
|
+
}, async (params) => {
|
|
9
|
+
try {
|
|
10
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
11
|
+
const args = ["rules", "init"];
|
|
12
|
+
if (params.flutter) {
|
|
13
|
+
args.push("--flutter");
|
|
14
|
+
}
|
|
15
|
+
if (params.output_dir) {
|
|
16
|
+
args.push("-o", params.output_dir);
|
|
17
|
+
}
|
|
18
|
+
const result = await runCli("jsonui-doc", args, { cwd: projectDir });
|
|
19
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("doc_rules_show", "Show current effective validation rules (base rules + custom overrides)", {
|
|
5
|
+
directory: z.string().optional().describe("Directory to search for .jsonui-doc-rules.json"),
|
|
6
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
7
|
+
}, async (params) => {
|
|
8
|
+
try {
|
|
9
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
10
|
+
const args = ["rules", "show"];
|
|
11
|
+
if (params.directory) {
|
|
12
|
+
args.push("-d", params.directory);
|
|
13
|
+
}
|
|
14
|
+
const result = await runCli("jsonui-doc", args, { cwd: projectDir });
|
|
15
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|