@tekton-ui/mcp-server 0.3.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/LICENSE +21 -0
- package/README.md +980 -0
- package/dist/auth/cache.d.ts +28 -0
- package/dist/auth/cache.d.ts.map +1 -0
- package/dist/auth/cache.js +48 -0
- package/dist/auth/cache.js.map +1 -0
- package/dist/auth/guard.d.ts +13 -0
- package/dist/auth/guard.d.ts.map +1 -0
- package/dist/auth/guard.js +21 -0
- package/dist/auth/guard.js.map +1 -0
- package/dist/auth/state.d.ts +32 -0
- package/dist/auth/state.d.ts.map +1 -0
- package/dist/auth/state.js +72 -0
- package/dist/auth/state.js.map +1 -0
- package/dist/auth/theme-access.d.ts +10 -0
- package/dist/auth/theme-access.d.ts.map +1 -0
- package/dist/auth/theme-access.js +24 -0
- package/dist/auth/theme-access.js.map +1 -0
- package/dist/auth/verify.d.ts +44 -0
- package/dist/auth/verify.d.ts.map +1 -0
- package/dist/auth/verify.js +77 -0
- package/dist/auth/verify.js.map +1 -0
- package/dist/cli/credentials.d.ts +29 -0
- package/dist/cli/credentials.d.ts.map +1 -0
- package/dist/cli/credentials.js +66 -0
- package/dist/cli/credentials.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +36 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/login.d.ts +9 -0
- package/dist/cli/login.d.ts.map +1 -0
- package/dist/cli/login.js +120 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/logout.d.ts +9 -0
- package/dist/cli/logout.d.ts.map +1 -0
- package/dist/cli/logout.js +18 -0
- package/dist/cli/logout.js.map +1 -0
- package/dist/cli/status.d.ts +9 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +31 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/data/component-registry.d.ts +30 -0
- package/dist/data/component-registry.d.ts.map +1 -0
- package/dist/data/component-registry.js +320 -0
- package/dist/data/component-registry.js.map +1 -0
- package/dist/data/examples/screen-examples.d.ts +38 -0
- package/dist/data/examples/screen-examples.d.ts.map +1 -0
- package/dist/data/examples/screen-examples.js +500 -0
- package/dist/data/examples/screen-examples.js.map +1 -0
- package/dist/data/hint-generator.d.ts +16 -0
- package/dist/data/hint-generator.d.ts.map +1 -0
- package/dist/data/hint-generator.js +298 -0
- package/dist/data/hint-generator.js.map +1 -0
- package/dist/data/recipe-resolver.d.ts +48 -0
- package/dist/data/recipe-resolver.d.ts.map +1 -0
- package/dist/data/recipe-resolver.js +226 -0
- package/dist/data/recipe-resolver.js.map +1 -0
- package/dist/data/template-matcher.d.ts +50 -0
- package/dist/data/template-matcher.d.ts.map +1 -0
- package/dist/data/template-matcher.js +240 -0
- package/dist/data/template-matcher.js.map +1 -0
- package/dist/generators/core-resolver.d.ts +56 -0
- package/dist/generators/core-resolver.d.ts.map +1 -0
- package/dist/generators/core-resolver.js +490 -0
- package/dist/generators/core-resolver.js.map +1 -0
- package/dist/generators/css-generator.d.ts +49 -0
- package/dist/generators/css-generator.d.ts.map +1 -0
- package/dist/generators/css-generator.js +294 -0
- package/dist/generators/css-generator.js.map +1 -0
- package/dist/generators/index.d.ts +13 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +16 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/llm-generator.d.ts +96 -0
- package/dist/generators/llm-generator.d.ts.map +1 -0
- package/dist/generators/llm-generator.js +296 -0
- package/dist/generators/llm-generator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +818 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/mcp-schemas.d.ts +4132 -0
- package/dist/schemas/mcp-schemas.d.ts.map +1 -0
- package/dist/schemas/mcp-schemas.js +946 -0
- package/dist/schemas/mcp-schemas.js.map +1 -0
- package/dist/storage/blueprint-storage.d.ts +68 -0
- package/dist/storage/blueprint-storage.d.ts.map +1 -0
- package/dist/storage/blueprint-storage.js +135 -0
- package/dist/storage/blueprint-storage.js.map +1 -0
- package/dist/storage/timestamp-manager.d.ts +32 -0
- package/dist/storage/timestamp-manager.d.ts.map +1 -0
- package/dist/storage/timestamp-manager.js +59 -0
- package/dist/storage/timestamp-manager.js.map +1 -0
- package/dist/tools/export-screen.d.ts +34 -0
- package/dist/tools/export-screen.d.ts.map +1 -0
- package/dist/tools/export-screen.js +344 -0
- package/dist/tools/export-screen.js.map +1 -0
- package/dist/tools/generate-blueprint.d.ts +15 -0
- package/dist/tools/generate-blueprint.d.ts.map +1 -0
- package/dist/tools/generate-blueprint.js +165 -0
- package/dist/tools/generate-blueprint.js.map +1 -0
- package/dist/tools/generate-screen.d.ts +13 -0
- package/dist/tools/generate-screen.d.ts.map +1 -0
- package/dist/tools/generate-screen.js +82 -0
- package/dist/tools/generate-screen.js.map +1 -0
- package/dist/tools/get-screen-generation-context.d.ts +11 -0
- package/dist/tools/get-screen-generation-context.d.ts.map +1 -0
- package/dist/tools/get-screen-generation-context.js +316 -0
- package/dist/tools/get-screen-generation-context.js.map +1 -0
- package/dist/tools/list-components.d.ts +15 -0
- package/dist/tools/list-components.d.ts.map +1 -0
- package/dist/tools/list-components.js +46 -0
- package/dist/tools/list-components.js.map +1 -0
- package/dist/tools/list-icon-libraries.d.ts +12 -0
- package/dist/tools/list-icon-libraries.d.ts.map +1 -0
- package/dist/tools/list-icon-libraries.js +48 -0
- package/dist/tools/list-icon-libraries.js.map +1 -0
- package/dist/tools/list-screen-templates.d.ts +15 -0
- package/dist/tools/list-screen-templates.d.ts.map +1 -0
- package/dist/tools/list-screen-templates.js +63 -0
- package/dist/tools/list-screen-templates.js.map +1 -0
- package/dist/tools/list-themes.d.ts +13 -0
- package/dist/tools/list-themes.d.ts.map +1 -0
- package/dist/tools/list-themes.js +42 -0
- package/dist/tools/list-themes.js.map +1 -0
- package/dist/tools/list-tokens.d.ts +13 -0
- package/dist/tools/list-tokens.d.ts.map +1 -0
- package/dist/tools/list-tokens.js +92 -0
- package/dist/tools/list-tokens.js.map +1 -0
- package/dist/tools/preview-component.d.ts +18 -0
- package/dist/tools/preview-component.d.ts.map +1 -0
- package/dist/tools/preview-component.js +178 -0
- package/dist/tools/preview-component.js.map +1 -0
- package/dist/tools/preview-icon-library.d.ts +13 -0
- package/dist/tools/preview-icon-library.d.ts.map +1 -0
- package/dist/tools/preview-icon-library.js +63 -0
- package/dist/tools/preview-icon-library.js.map +1 -0
- package/dist/tools/preview-screen-template.d.ts +18 -0
- package/dist/tools/preview-screen-template.d.ts.map +1 -0
- package/dist/tools/preview-screen-template.js +101 -0
- package/dist/tools/preview-screen-template.js.map +1 -0
- package/dist/tools/preview-theme.d.ts +15 -0
- package/dist/tools/preview-theme.d.ts.map +1 -0
- package/dist/tools/preview-theme.js +71 -0
- package/dist/tools/preview-theme.js.map +1 -0
- package/dist/tools/validate-environment.d.ts +37 -0
- package/dist/tools/validate-environment.d.ts.map +1 -0
- package/dist/tools/validate-environment.js +153 -0
- package/dist/tools/validate-environment.js.map +1 -0
- package/dist/tools/validate-screen-definition.d.ts +10 -0
- package/dist/tools/validate-screen-definition.d.ts.map +1 -0
- package/dist/tools/validate-screen-definition.js +463 -0
- package/dist/tools/validate-screen-definition.js.map +1 -0
- package/dist/tools/validate-screen.d.ts +13 -0
- package/dist/tools/validate-screen.d.ts.map +1 -0
- package/dist/tools/validate-screen.js +106 -0
- package/dist/tools/validate-screen.js.map +1 -0
- package/dist/utils/dependency-extractor.d.ts +13 -0
- package/dist/utils/dependency-extractor.d.ts.map +1 -0
- package/dist/utils/dependency-extractor.js +232 -0
- package/dist/utils/dependency-extractor.js.map +1 -0
- package/dist/utils/error-handler.d.ts +29 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +48 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +14 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/package-json-reader.d.ts +37 -0
- package/dist/utils/package-json-reader.d.ts.map +1 -0
- package/dist/utils/package-json-reader.js +108 -0
- package/dist/utils/package-json-reader.js.map +1 -0
- package/dist/utils/tailwind-config-reader.d.ts +23 -0
- package/dist/utils/tailwind-config-reader.d.ts.map +1 -0
- package/dist/utils/tailwind-config-reader.js +81 -0
- package/dist/utils/tailwind-config-reader.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Environment MCP Tool
|
|
3
|
+
* SPEC-MCP-005 Phase 2: Check if user's project has required NPM packages installed
|
|
4
|
+
* SPEC-MCP-005: Tailwind CSS 설정 검증 확장
|
|
5
|
+
*/
|
|
6
|
+
import type { ValidateEnvironmentInput, ValidateEnvironmentOutput } from '../schemas/mcp-schemas.js';
|
|
7
|
+
/**
|
|
8
|
+
* Validate user's environment for required dependencies
|
|
9
|
+
*
|
|
10
|
+
* Compares required packages against installed packages in package.json
|
|
11
|
+
* and provides installation commands for missing packages.
|
|
12
|
+
* Optionally validates Tailwind CSS configuration for @tekton-ui/ui compatibility.
|
|
13
|
+
*
|
|
14
|
+
* @param input - Project path and required packages to validate
|
|
15
|
+
* @returns Validation result with installed/missing packages and install commands
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const result = await validateEnvironmentTool({
|
|
20
|
+
* projectPath: '/path/to/project',
|
|
21
|
+
* requiredPackages: ['framer-motion', 'react'],
|
|
22
|
+
* checkTailwind: true
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* if (result.success && result.missing.length > 0) {
|
|
26
|
+
* console.log(`Missing packages: ${result.missing.join(', ')}`);
|
|
27
|
+
* console.log(`Install with: ${result.installCommands.npm}`);
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* if (result.tailwind?.issues.length) {
|
|
31
|
+
* console.log('Tailwind issues:', result.tailwind.issues);
|
|
32
|
+
* console.log('Fixes:', result.tailwind.fixes);
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateEnvironmentTool(input: ValidateEnvironmentInput): Promise<ValidateEnvironmentOutput>;
|
|
37
|
+
//# sourceMappingURL=validate-environment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-environment.d.ts","sourceRoot":"","sources":["../../src/tools/validate-environment.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,2BAA2B,CAAC;AAKnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,yBAAyB,CAAC,CAqGpC"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Environment MCP Tool
|
|
3
|
+
* SPEC-MCP-005 Phase 2: Check if user's project has required NPM packages installed
|
|
4
|
+
* SPEC-MCP-005: Tailwind CSS 설정 검증 확장
|
|
5
|
+
*/
|
|
6
|
+
import { readPackageJson } from '../utils/package-json-reader.js';
|
|
7
|
+
import { readTailwindConfig } from '../utils/tailwind-config-reader.js';
|
|
8
|
+
import { extractErrorMessage } from '../utils/error-handler.js';
|
|
9
|
+
/**
|
|
10
|
+
* Validate user's environment for required dependencies
|
|
11
|
+
*
|
|
12
|
+
* Compares required packages against installed packages in package.json
|
|
13
|
+
* and provides installation commands for missing packages.
|
|
14
|
+
* Optionally validates Tailwind CSS configuration for @tekton-ui/ui compatibility.
|
|
15
|
+
*
|
|
16
|
+
* @param input - Project path and required packages to validate
|
|
17
|
+
* @returns Validation result with installed/missing packages and install commands
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const result = await validateEnvironmentTool({
|
|
22
|
+
* projectPath: '/path/to/project',
|
|
23
|
+
* requiredPackages: ['framer-motion', 'react'],
|
|
24
|
+
* checkTailwind: true
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* if (result.success && result.missing.length > 0) {
|
|
28
|
+
* console.log(`Missing packages: ${result.missing.join(', ')}`);
|
|
29
|
+
* console.log(`Install with: ${result.installCommands.npm}`);
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* if (result.tailwind?.issues.length) {
|
|
33
|
+
* console.log('Tailwind issues:', result.tailwind.issues);
|
|
34
|
+
* console.log('Fixes:', result.tailwind.fixes);
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export async function validateEnvironmentTool(input) {
|
|
39
|
+
try {
|
|
40
|
+
const { projectPath, requiredPackages, checkTailwind } = input;
|
|
41
|
+
// Step 1: Read package.json from the project
|
|
42
|
+
const readResult = readPackageJson(projectPath);
|
|
43
|
+
if (!readResult.success || !readResult.installedPackages) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
error: readResult.error || 'Failed to read package.json',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const installedPackages = readResult.installedPackages;
|
|
50
|
+
// Step 2: Compare required packages with installed packages
|
|
51
|
+
const installed = {};
|
|
52
|
+
const missing = [];
|
|
53
|
+
for (const packageName of requiredPackages) {
|
|
54
|
+
const version = installedPackages[packageName];
|
|
55
|
+
if (version !== undefined) {
|
|
56
|
+
// Package is installed
|
|
57
|
+
installed[packageName] = version;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Package is missing
|
|
61
|
+
missing.push(packageName);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Step 3: Generate install commands for missing packages
|
|
65
|
+
const installCommands = generateInstallCommands(missing);
|
|
66
|
+
// Step 4: Check for potential warnings (optional enhancement)
|
|
67
|
+
const warnings = [];
|
|
68
|
+
// Step 5: Tailwind CSS 설정 검증
|
|
69
|
+
let tailwind;
|
|
70
|
+
if (checkTailwind !== false) {
|
|
71
|
+
const tailwindResult = readTailwindConfig(projectPath);
|
|
72
|
+
const issues = [];
|
|
73
|
+
const fixes = [];
|
|
74
|
+
if (!tailwindResult.found) {
|
|
75
|
+
issues.push('tailwind.config.{ts,js,mjs,cjs} not found in project root');
|
|
76
|
+
fixes.push('Create a tailwind.config.ts file in your project root. ' +
|
|
77
|
+
'See https://tailwindcss.com/docs/configuration for setup guide.');
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (!tailwindResult.hasUiContentPath) {
|
|
81
|
+
issues.push('tailwind.config content paths do not include @tekton-ui/ui — ' +
|
|
82
|
+
'component styles (Dialog, AlertDialog, Popover, etc.) will not be compiled');
|
|
83
|
+
fixes.push("Add '../../packages/ui/src/**/*.{ts,tsx}' (monorepo) or " +
|
|
84
|
+
"'node_modules/@tekton-ui/ui/dist/**/*.{js,ts,jsx,tsx}' (standalone) " +
|
|
85
|
+
'to the content array in your tailwind.config');
|
|
86
|
+
}
|
|
87
|
+
if (!tailwindResult.hasAnimatePlugin) {
|
|
88
|
+
issues.push('tailwindcss-animate plugin is not configured — ' +
|
|
89
|
+
'Radix UI component animations (Dialog open/close, Popover, Tooltip) will not work');
|
|
90
|
+
fixes.push('Install tailwindcss-animate (npm install tailwindcss-animate) and add it to plugins array: ' +
|
|
91
|
+
"import animate from 'tailwindcss-animate'; plugins: [animate]");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
tailwind = {
|
|
95
|
+
configFound: tailwindResult.found,
|
|
96
|
+
configPath: tailwindResult.configPath,
|
|
97
|
+
hasUiContentPath: tailwindResult.hasUiContentPath,
|
|
98
|
+
hasAnimatePlugin: tailwindResult.hasAnimatePlugin,
|
|
99
|
+
issues,
|
|
100
|
+
fixes,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
success: true,
|
|
105
|
+
installed,
|
|
106
|
+
missing,
|
|
107
|
+
installCommands,
|
|
108
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
109
|
+
tailwind,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: extractErrorMessage(error),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Generate installation commands for multiple package managers
|
|
121
|
+
*
|
|
122
|
+
* @param packages - Array of package names to install
|
|
123
|
+
* @returns Install commands for npm, yarn, pnpm, and bun
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const commands = generateInstallCommands(['react', 'react-dom']);
|
|
128
|
+
* // {
|
|
129
|
+
* // npm: 'npm install react react-dom',
|
|
130
|
+
* // yarn: 'yarn add react react-dom',
|
|
131
|
+
* // pnpm: 'pnpm add react react-dom',
|
|
132
|
+
* // bun: 'bun add react react-dom'
|
|
133
|
+
* // }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
function generateInstallCommands(packages) {
|
|
137
|
+
if (packages.length === 0) {
|
|
138
|
+
return {
|
|
139
|
+
npm: '',
|
|
140
|
+
yarn: '',
|
|
141
|
+
pnpm: '',
|
|
142
|
+
bun: '',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const packageList = packages.join(' ');
|
|
146
|
+
return {
|
|
147
|
+
npm: `npm install ${packageList}`,
|
|
148
|
+
yarn: `yarn add ${packageList}`,
|
|
149
|
+
pnpm: `pnpm add ${packageList}`,
|
|
150
|
+
bun: `bun add ${packageList}`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=validate-environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-environment.js","sourceRoot":"","sources":["../../src/tools/validate-environment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Screen Definition MCP Tool
|
|
3
|
+
* SPEC-MCP-004 Phase 3.5: Validates screen definitions with helpful feedback
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidateScreenDefinitionInput, ValidateScreenDefinitionOutput } from '../schemas/mcp-schemas.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validate Screen Definition tool implementation
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateScreenDefinitionTool(input: ValidateScreenDefinitionInput): Promise<ValidateScreenDefinitionOutput>;
|
|
10
|
+
//# sourceMappingURL=validate-screen-definition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-screen-definition.d.ts","sourceRoot":"","sources":["../../src/tools/validate-screen-definition.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACV,6BAA6B,EAC7B,8BAA8B,EAI/B,MAAM,2BAA2B,CAAC;AA2anC;;GAEG;AACH,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,6BAA6B,GACnC,OAAO,CAAC,8BAA8B,CAAC,CAkGzC"}
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Screen Definition MCP Tool
|
|
3
|
+
* SPEC-MCP-004 Phase 3.5: Validates screen definitions with helpful feedback
|
|
4
|
+
*/
|
|
5
|
+
import { COMPONENT_CATALOG } from '@tekton-ui/core';
|
|
6
|
+
import { ScreenDefinitionSchema } from '../schemas/mcp-schemas.js';
|
|
7
|
+
import { extractErrorMessage } from '../utils/error-handler.js';
|
|
8
|
+
/**
|
|
9
|
+
* Valid shell tokens from SPEC-LAYOUT-001 and SPEC-LAYOUT-004
|
|
10
|
+
*/
|
|
11
|
+
const VALID_SHELLS = [
|
|
12
|
+
// Web shells
|
|
13
|
+
'shell.web.app',
|
|
14
|
+
'shell.web.dashboard',
|
|
15
|
+
'shell.web.auth',
|
|
16
|
+
'shell.web.marketing',
|
|
17
|
+
'shell.web.minimal',
|
|
18
|
+
// Mobile shells (SPEC-LAYOUT-004)
|
|
19
|
+
'shell.mobile.app',
|
|
20
|
+
'shell.mobile.fullscreen',
|
|
21
|
+
'shell.mobile.modal',
|
|
22
|
+
'shell.mobile.tab',
|
|
23
|
+
'shell.mobile.drawer',
|
|
24
|
+
'shell.mobile.detail',
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Valid page tokens from SPEC-LAYOUT-001
|
|
28
|
+
*/
|
|
29
|
+
const VALID_PAGES = ['page.dashboard', 'page.detail', 'page.wizard', 'page.resource', 'page.empty'];
|
|
30
|
+
/**
|
|
31
|
+
* Valid section patterns from SPEC-LAYOUT-001
|
|
32
|
+
*/
|
|
33
|
+
const VALID_SECTION_PATTERNS = [
|
|
34
|
+
'section.container',
|
|
35
|
+
'section.centered',
|
|
36
|
+
'section.grid-2',
|
|
37
|
+
'section.grid-3',
|
|
38
|
+
'section.grid-4',
|
|
39
|
+
'section.split-50-50',
|
|
40
|
+
'section.split-60-40',
|
|
41
|
+
'section.split-70-30',
|
|
42
|
+
'section.hero',
|
|
43
|
+
'section.feature',
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Valid slot names
|
|
47
|
+
*/
|
|
48
|
+
const VALID_SLOTS = ['header', 'main', 'sidebar', 'footer'];
|
|
49
|
+
/**
|
|
50
|
+
* Get similar values for suggestions (Levenshtein distance based)
|
|
51
|
+
*/
|
|
52
|
+
function getSimilarValues(value, validValues) {
|
|
53
|
+
const similar = [];
|
|
54
|
+
for (const valid of validValues) {
|
|
55
|
+
const distance = levenshteinDistance(value.toLowerCase(), valid.toLowerCase());
|
|
56
|
+
if (distance <= 3) {
|
|
57
|
+
similar.push({ value: valid, distance });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return similar
|
|
61
|
+
.sort((a, b) => a.distance - b.distance)
|
|
62
|
+
.slice(0, 3)
|
|
63
|
+
.map(s => s.value);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Simple Levenshtein distance implementation
|
|
67
|
+
*/
|
|
68
|
+
function levenshteinDistance(a, b) {
|
|
69
|
+
const matrix = [];
|
|
70
|
+
for (let i = 0; i <= b.length; i++) {
|
|
71
|
+
matrix[i] = [i];
|
|
72
|
+
}
|
|
73
|
+
for (let j = 0; j <= a.length; j++) {
|
|
74
|
+
matrix[0][j] = j;
|
|
75
|
+
}
|
|
76
|
+
for (let i = 1; i <= b.length; i++) {
|
|
77
|
+
for (let j = 1; j <= a.length; j++) {
|
|
78
|
+
const cost = b.charAt(i - 1) === a.charAt(j - 1) ? 0 : 1;
|
|
79
|
+
matrix[i][j] = Math.min((matrix[i - 1]?.[j] ?? 0) + 1, (matrix[i]?.[j - 1] ?? 0) + 1, (matrix[i - 1]?.[j - 1] ?? 0) + cost);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return matrix[b.length]?.[a.length] ?? 0;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validate shell token
|
|
86
|
+
*/
|
|
87
|
+
function validateShell(shell, path) {
|
|
88
|
+
const errors = [];
|
|
89
|
+
const warnings = [];
|
|
90
|
+
if (!shell.startsWith('shell.')) {
|
|
91
|
+
errors.push({
|
|
92
|
+
path,
|
|
93
|
+
code: 'INVALID_SHELL_FORMAT',
|
|
94
|
+
message: 'Shell token must start with "shell."',
|
|
95
|
+
expected: 'shell.{platform}.{name}',
|
|
96
|
+
received: shell,
|
|
97
|
+
suggestion: `Use format: shell.web.app or shell.mobile.app`,
|
|
98
|
+
});
|
|
99
|
+
return { errors, warnings };
|
|
100
|
+
}
|
|
101
|
+
if (!VALID_SHELLS.includes(shell)) {
|
|
102
|
+
const similar = getSimilarValues(shell, VALID_SHELLS);
|
|
103
|
+
errors.push({
|
|
104
|
+
path,
|
|
105
|
+
code: 'UNKNOWN_SHELL',
|
|
106
|
+
message: `Unknown shell token: "${shell}"`,
|
|
107
|
+
expected: VALID_SHELLS.slice(0, 5).join(', ') + '...',
|
|
108
|
+
received: shell,
|
|
109
|
+
suggestion: similar.length > 0
|
|
110
|
+
? `Did you mean: ${similar.join(', ')}?`
|
|
111
|
+
: `Valid shells: ${VALID_SHELLS.join(', ')}`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return { errors, warnings };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Validate page token
|
|
118
|
+
*/
|
|
119
|
+
function validatePage(page, path) {
|
|
120
|
+
const errors = [];
|
|
121
|
+
const warnings = [];
|
|
122
|
+
if (!page.startsWith('page.')) {
|
|
123
|
+
errors.push({
|
|
124
|
+
path,
|
|
125
|
+
code: 'INVALID_PAGE_FORMAT',
|
|
126
|
+
message: 'Page token must start with "page."',
|
|
127
|
+
expected: 'page.{name}',
|
|
128
|
+
received: page,
|
|
129
|
+
suggestion: 'Use format: page.dashboard, page.detail, etc.',
|
|
130
|
+
});
|
|
131
|
+
return { errors, warnings };
|
|
132
|
+
}
|
|
133
|
+
if (!VALID_PAGES.includes(page)) {
|
|
134
|
+
const similar = getSimilarValues(page, VALID_PAGES);
|
|
135
|
+
errors.push({
|
|
136
|
+
path,
|
|
137
|
+
code: 'UNKNOWN_PAGE',
|
|
138
|
+
message: `Unknown page token: "${page}"`,
|
|
139
|
+
expected: VALID_PAGES.join(', '),
|
|
140
|
+
received: page,
|
|
141
|
+
suggestion: similar.length > 0
|
|
142
|
+
? `Did you mean: ${similar.join(', ')}?`
|
|
143
|
+
: `Valid pages: ${VALID_PAGES.join(', ')}`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return { errors, warnings };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Validate section pattern
|
|
150
|
+
*/
|
|
151
|
+
function validateSectionPattern(pattern, path, strict) {
|
|
152
|
+
const errors = [];
|
|
153
|
+
const warnings = [];
|
|
154
|
+
if (!pattern.startsWith('section.')) {
|
|
155
|
+
errors.push({
|
|
156
|
+
path,
|
|
157
|
+
code: 'INVALID_SECTION_FORMAT',
|
|
158
|
+
message: 'Section pattern must start with "section."',
|
|
159
|
+
expected: 'section.{name}',
|
|
160
|
+
received: pattern,
|
|
161
|
+
suggestion: 'Use format: section.container, section.grid-4, etc.',
|
|
162
|
+
});
|
|
163
|
+
return { errors, warnings };
|
|
164
|
+
}
|
|
165
|
+
if (!VALID_SECTION_PATTERNS.includes(pattern)) {
|
|
166
|
+
if (strict) {
|
|
167
|
+
const similar = getSimilarValues(pattern, VALID_SECTION_PATTERNS);
|
|
168
|
+
errors.push({
|
|
169
|
+
path,
|
|
170
|
+
code: 'UNKNOWN_SECTION_PATTERN',
|
|
171
|
+
message: `Unknown section pattern: "${pattern}"`,
|
|
172
|
+
expected: VALID_SECTION_PATTERNS.slice(0, 5).join(', ') + '...',
|
|
173
|
+
received: pattern,
|
|
174
|
+
suggestion: similar.length > 0
|
|
175
|
+
? `Did you mean: ${similar.join(', ')}?`
|
|
176
|
+
: `Valid patterns: ${VALID_SECTION_PATTERNS.join(', ')}`,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
warnings.push({
|
|
181
|
+
path,
|
|
182
|
+
code: 'CUSTOM_SECTION_PATTERN',
|
|
183
|
+
message: `Custom section pattern "${pattern}" - ensure it's defined in your layout system`,
|
|
184
|
+
recommendation: `Consider using standard patterns: ${VALID_SECTION_PATTERNS.slice(0, 5).join(', ')}`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return { errors, warnings };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Validate component type
|
|
192
|
+
*/
|
|
193
|
+
function validateComponentType(type, path, strict) {
|
|
194
|
+
const errors = [];
|
|
195
|
+
const warnings = [];
|
|
196
|
+
// Check if component type exists in catalog (case-insensitive comparison)
|
|
197
|
+
const catalogLower = COMPONENT_CATALOG.map(c => c.toLowerCase());
|
|
198
|
+
const typeLower = type.toLowerCase();
|
|
199
|
+
if (!catalogLower.includes(typeLower)) {
|
|
200
|
+
const similar = getSimilarValues(type, COMPONENT_CATALOG);
|
|
201
|
+
if (strict) {
|
|
202
|
+
errors.push({
|
|
203
|
+
path,
|
|
204
|
+
code: 'UNKNOWN_COMPONENT',
|
|
205
|
+
message: `Unknown component type: "${type}"`,
|
|
206
|
+
expected: 'A component from @tekton-ui/ui catalog',
|
|
207
|
+
received: type,
|
|
208
|
+
suggestion: similar.length > 0
|
|
209
|
+
? `Did you mean: ${similar.join(', ')}?`
|
|
210
|
+
: 'Use list-components tool to see available components',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
warnings.push({
|
|
215
|
+
path,
|
|
216
|
+
code: 'CUSTOM_COMPONENT',
|
|
217
|
+
message: `Component "${type}" not found in catalog - ensure it's a valid custom component`,
|
|
218
|
+
recommendation: similar.length > 0
|
|
219
|
+
? `Did you mean: ${similar.join(', ')}?`
|
|
220
|
+
: 'Use list-components tool to see available components',
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
// Check casing
|
|
226
|
+
const correctCase = COMPONENT_CATALOG.find(c => c.toLowerCase() === typeLower);
|
|
227
|
+
if (correctCase && correctCase !== type) {
|
|
228
|
+
warnings.push({
|
|
229
|
+
path,
|
|
230
|
+
code: 'COMPONENT_CASE',
|
|
231
|
+
message: `Component type "${type}" has incorrect casing`,
|
|
232
|
+
recommendation: `Use "${correctCase}" instead`,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { errors, warnings };
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Validate slot value
|
|
240
|
+
*/
|
|
241
|
+
function validateSlot(slot, path) {
|
|
242
|
+
const errors = [];
|
|
243
|
+
const warnings = [];
|
|
244
|
+
if (!VALID_SLOTS.includes(slot)) {
|
|
245
|
+
const similar = getSimilarValues(slot, VALID_SLOTS);
|
|
246
|
+
errors.push({
|
|
247
|
+
path,
|
|
248
|
+
code: 'INVALID_SLOT',
|
|
249
|
+
message: `Invalid slot value: "${slot}"`,
|
|
250
|
+
expected: VALID_SLOTS.join(', '),
|
|
251
|
+
received: slot,
|
|
252
|
+
suggestion: similar.length > 0
|
|
253
|
+
? `Did you mean: ${similar.join(', ')}?`
|
|
254
|
+
: `Valid slots: ${VALID_SLOTS.join(', ')}`,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return { errors, warnings };
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Generate improvement suggestions
|
|
261
|
+
*/
|
|
262
|
+
function generateSuggestions(definition) {
|
|
263
|
+
const suggestions = [];
|
|
264
|
+
// Handle null/undefined definition
|
|
265
|
+
if (!definition || typeof definition !== 'object') {
|
|
266
|
+
return suggestions;
|
|
267
|
+
}
|
|
268
|
+
// Check for missing name/description
|
|
269
|
+
if (!definition.name) {
|
|
270
|
+
suggestions.push({
|
|
271
|
+
category: 'maintainability',
|
|
272
|
+
message: 'Consider adding a human-readable name for better documentation',
|
|
273
|
+
affectedPath: 'name',
|
|
274
|
+
suggestedChange: 'Add a descriptive name property',
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (!definition.description) {
|
|
278
|
+
suggestions.push({
|
|
279
|
+
category: 'maintainability',
|
|
280
|
+
message: 'Consider adding a description for documentation purposes',
|
|
281
|
+
affectedPath: 'description',
|
|
282
|
+
suggestedChange: 'Add a brief description of the screen purpose',
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
// Check for missing themeId
|
|
286
|
+
if (!definition.themeId) {
|
|
287
|
+
suggestions.push({
|
|
288
|
+
category: 'consistency',
|
|
289
|
+
message: 'Consider specifying a themeId for consistent styling',
|
|
290
|
+
affectedPath: 'themeId',
|
|
291
|
+
suggestedChange: 'Add themeId to enable theme recipe application',
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
// Check sections
|
|
295
|
+
if (definition.sections && Array.isArray(definition.sections)) {
|
|
296
|
+
// Check for empty sections
|
|
297
|
+
for (let i = 0; i < definition.sections.length; i++) {
|
|
298
|
+
const section = definition.sections[i];
|
|
299
|
+
if (!section.components || section.components.length === 0) {
|
|
300
|
+
suggestions.push({
|
|
301
|
+
category: 'maintainability',
|
|
302
|
+
message: `Section "${section.id || i}" has no components`,
|
|
303
|
+
affectedPath: `sections[${i}].components`,
|
|
304
|
+
suggestedChange: 'Add components or remove empty section',
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
// Check for missing slot
|
|
308
|
+
if (!section.slot) {
|
|
309
|
+
suggestions.push({
|
|
310
|
+
category: 'consistency',
|
|
311
|
+
message: `Section "${section.id || i}" has no slot assigned`,
|
|
312
|
+
affectedPath: `sections[${i}].slot`,
|
|
313
|
+
suggestedChange: 'Assign to a slot (header, main, sidebar, footer) for proper layout positioning',
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
// Check components for accessibility
|
|
317
|
+
if (section.components && Array.isArray(section.components)) {
|
|
318
|
+
for (let j = 0; j < section.components.length; j++) {
|
|
319
|
+
const component = section.components[j];
|
|
320
|
+
// Check images for alt text
|
|
321
|
+
if ((component.type === 'Image' || component.type === 'Avatar') &&
|
|
322
|
+
(!component.props || !component.props.alt)) {
|
|
323
|
+
suggestions.push({
|
|
324
|
+
category: 'accessibility',
|
|
325
|
+
message: `${component.type} component is missing alt text`,
|
|
326
|
+
affectedPath: `sections[${i}].components[${j}].props.alt`,
|
|
327
|
+
suggestedChange: 'Add descriptive alt text for screen readers',
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
// Check inputs for labels
|
|
331
|
+
if (component.type === 'Input' &&
|
|
332
|
+
component.props &&
|
|
333
|
+
!component.props.label &&
|
|
334
|
+
!component.props['aria-label']) {
|
|
335
|
+
suggestions.push({
|
|
336
|
+
category: 'accessibility',
|
|
337
|
+
message: 'Input component is missing a label',
|
|
338
|
+
affectedPath: `sections[${i}].components[${j}].props.label`,
|
|
339
|
+
suggestedChange: 'Add a label prop or aria-label for accessibility',
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Check for no main slot
|
|
346
|
+
const hasMainSlot = definition.sections.some((s) => s.slot === 'main' || !s.slot);
|
|
347
|
+
if (!hasMainSlot) {
|
|
348
|
+
suggestions.push({
|
|
349
|
+
category: 'consistency',
|
|
350
|
+
message: 'No section assigned to main slot',
|
|
351
|
+
affectedPath: 'sections',
|
|
352
|
+
suggestedChange: 'Assign at least one section to the main slot',
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return suggestions;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Validate Screen Definition tool implementation
|
|
360
|
+
*/
|
|
361
|
+
export async function validateScreenDefinitionTool(input) {
|
|
362
|
+
try {
|
|
363
|
+
const errors = [];
|
|
364
|
+
const warnings = [];
|
|
365
|
+
const strict = input.strict !== false; // Default to strict
|
|
366
|
+
const definition = input.definition;
|
|
367
|
+
// 1. Validate with Zod schema first
|
|
368
|
+
const zodResult = ScreenDefinitionSchema.safeParse(definition);
|
|
369
|
+
if (!zodResult.success) {
|
|
370
|
+
for (const issue of zodResult.error.issues) {
|
|
371
|
+
errors.push({
|
|
372
|
+
path: issue.path.join('.'),
|
|
373
|
+
code: issue.code,
|
|
374
|
+
message: issue.message,
|
|
375
|
+
suggestion: getZodSuggestion(issue),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// 2. Additional semantic validation (only if basic structure is valid)
|
|
380
|
+
if (definition && typeof definition === 'object') {
|
|
381
|
+
// Validate shell
|
|
382
|
+
if (definition.shell) {
|
|
383
|
+
const shellResult = validateShell(definition.shell, 'shell');
|
|
384
|
+
errors.push(...shellResult.errors);
|
|
385
|
+
warnings.push(...shellResult.warnings);
|
|
386
|
+
}
|
|
387
|
+
// Validate page
|
|
388
|
+
if (definition.page) {
|
|
389
|
+
const pageResult = validatePage(definition.page, 'page');
|
|
390
|
+
errors.push(...pageResult.errors);
|
|
391
|
+
warnings.push(...pageResult.warnings);
|
|
392
|
+
}
|
|
393
|
+
// Validate sections
|
|
394
|
+
if (definition.sections && Array.isArray(definition.sections)) {
|
|
395
|
+
for (let i = 0; i < definition.sections.length; i++) {
|
|
396
|
+
const section = definition.sections[i];
|
|
397
|
+
// Validate pattern
|
|
398
|
+
if (section.pattern) {
|
|
399
|
+
const patternResult = validateSectionPattern(section.pattern, `sections[${i}].pattern`, strict);
|
|
400
|
+
errors.push(...patternResult.errors);
|
|
401
|
+
warnings.push(...patternResult.warnings);
|
|
402
|
+
}
|
|
403
|
+
// Validate slot
|
|
404
|
+
if (section.slot) {
|
|
405
|
+
const slotResult = validateSlot(section.slot, `sections[${i}].slot`);
|
|
406
|
+
errors.push(...slotResult.errors);
|
|
407
|
+
warnings.push(...slotResult.warnings);
|
|
408
|
+
}
|
|
409
|
+
// Validate components
|
|
410
|
+
if (section.components && Array.isArray(section.components)) {
|
|
411
|
+
for (let j = 0; j < section.components.length; j++) {
|
|
412
|
+
const component = section.components[j];
|
|
413
|
+
if (component.type) {
|
|
414
|
+
const typeResult = validateComponentType(component.type, `sections[${i}].components[${j}].type`, strict);
|
|
415
|
+
errors.push(...typeResult.errors);
|
|
416
|
+
warnings.push(...typeResult.warnings);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// 3. Generate improvement suggestions
|
|
424
|
+
const suggestions = generateSuggestions(definition);
|
|
425
|
+
const isValid = errors.length === 0;
|
|
426
|
+
return {
|
|
427
|
+
success: true,
|
|
428
|
+
valid: isValid,
|
|
429
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
430
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
431
|
+
suggestions: suggestions.length > 0 ? suggestions : undefined,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
return {
|
|
436
|
+
success: false,
|
|
437
|
+
error: extractErrorMessage(error),
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Get suggestion for Zod validation issue
|
|
443
|
+
*/
|
|
444
|
+
function getZodSuggestion(issue) {
|
|
445
|
+
switch (issue.code) {
|
|
446
|
+
case 'invalid_type':
|
|
447
|
+
return `Expected ${issue.expected}, but received ${issue.received}`;
|
|
448
|
+
case 'invalid_string':
|
|
449
|
+
if (issue.validation === 'regex') {
|
|
450
|
+
return 'Check the format - see the pattern in error message';
|
|
451
|
+
}
|
|
452
|
+
return 'Provide a valid string value';
|
|
453
|
+
case 'too_small':
|
|
454
|
+
return `Minimum ${issue.minimum} ${issue.type} required`;
|
|
455
|
+
case 'too_big':
|
|
456
|
+
return `Maximum ${issue.maximum} ${issue.type} allowed`;
|
|
457
|
+
case 'invalid_enum_value':
|
|
458
|
+
return `Valid values: ${issue.options.join(', ')}`;
|
|
459
|
+
default:
|
|
460
|
+
return 'Check the value format and try again';
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
//# sourceMappingURL=validate-screen-definition.js.map
|