universal-dev-standards 5.1.0-beta.6 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +6 -0
  2. package/bin/uds.js +14 -0
  3. package/bundled/ai/standards/agent-communication-protocol.ai.yaml +34 -0
  4. package/bundled/ai/standards/anti-sycophancy-prompting.ai.yaml +111 -0
  5. package/bundled/ai/standards/capability-declaration.ai.yaml +113 -0
  6. package/bundled/ai/standards/circuit-breaker.ai.yaml +93 -0
  7. package/bundled/ai/standards/developer-memory.ai.yaml +13 -0
  8. package/bundled/ai/standards/dual-phase-output.ai.yaml +108 -0
  9. package/bundled/ai/standards/failure-source-taxonomy.ai.yaml +115 -0
  10. package/bundled/ai/standards/frontend-design-standards.ai.yaml +305 -0
  11. package/bundled/ai/standards/health-check-standards.ai.yaml +140 -0
  12. package/bundled/ai/standards/immutability-first.ai.yaml +112 -0
  13. package/bundled/ai/standards/model-selection.ai.yaml +111 -3
  14. package/bundled/ai/standards/packaging-standards.ai.yaml +142 -0
  15. package/bundled/ai/standards/recovery-recipe-registry.ai.yaml +200 -0
  16. package/bundled/ai/standards/retry-standards.ai.yaml +134 -0
  17. package/bundled/ai/standards/security-decision.ai.yaml +87 -0
  18. package/bundled/ai/standards/skill-standard-alignment-check.ai.yaml +119 -0
  19. package/bundled/ai/standards/standard-admission-criteria.ai.yaml +107 -0
  20. package/bundled/ai/standards/standard-lifecycle-management.ai.yaml +144 -0
  21. package/bundled/ai/standards/timeout-standards.ai.yaml +104 -0
  22. package/bundled/ai/standards/token-budget.ai.yaml +108 -0
  23. package/bundled/ai/standards/translation-lifecycle-standards.ai.yaml +145 -0
  24. package/bundled/core/anti-sycophancy-prompting.md +184 -0
  25. package/bundled/core/capability-declaration.md +59 -0
  26. package/bundled/core/circuit-breaker.md +58 -0
  27. package/bundled/core/developer-memory.md +29 -1
  28. package/bundled/core/dual-phase-output.md +56 -0
  29. package/bundled/core/failure-source-taxonomy.md +72 -0
  30. package/bundled/core/frontend-design-standards.md +474 -0
  31. package/bundled/core/health-check-standards.md +72 -0
  32. package/bundled/core/immutability-first.md +105 -0
  33. package/bundled/core/model-selection.md +80 -0
  34. package/bundled/core/packaging-standards.md +216 -0
  35. package/bundled/core/recovery-recipe-registry.md +69 -0
  36. package/bundled/core/retry-standards.md +62 -0
  37. package/bundled/core/security-decision.md +65 -0
  38. package/bundled/core/skill-standard-alignment-check.md +79 -0
  39. package/bundled/core/standard-admission-criteria.md +84 -0
  40. package/bundled/core/standard-lifecycle-management.md +94 -0
  41. package/bundled/core/timeout-standards.md +63 -0
  42. package/bundled/core/token-budget.md +58 -0
  43. package/bundled/core/translation-lifecycle-standards.md +162 -0
  44. package/bundled/locales/zh-CN/CHANGELOG.md +51 -3
  45. package/bundled/locales/zh-CN/README.md +1 -1
  46. package/bundled/locales/zh-CN/core/anti-hallucination.md +22 -3
  47. package/bundled/locales/zh-CN/core/anti-sycophancy-prompting.md +192 -0
  48. package/bundled/locales/zh-CN/core/capability-declaration.md +123 -0
  49. package/bundled/locales/zh-CN/core/circuit-breaker.md +106 -0
  50. package/bundled/locales/zh-CN/core/dual-phase-output.md +103 -0
  51. package/bundled/locales/zh-CN/core/failure-source-taxonomy.md +99 -0
  52. package/bundled/locales/zh-CN/core/frontend-design-standards.md +289 -0
  53. package/bundled/locales/zh-CN/core/health-check-standards.md +144 -0
  54. package/bundled/locales/zh-CN/core/immutability-first.md +96 -0
  55. package/bundled/locales/zh-CN/core/packaging-standards.md +224 -0
  56. package/bundled/locales/zh-CN/core/recovery-recipe-registry.md +146 -0
  57. package/bundled/locales/zh-CN/core/retry-standards.md +131 -0
  58. package/bundled/locales/zh-CN/core/security-decision.md +104 -0
  59. package/bundled/locales/zh-CN/core/skill-standard-alignment-check.md +112 -0
  60. package/bundled/locales/zh-CN/core/standard-admission-criteria.md +104 -0
  61. package/bundled/locales/zh-CN/core/standard-lifecycle-management.md +116 -0
  62. package/bundled/locales/zh-CN/core/timeout-standards.md +117 -0
  63. package/bundled/locales/zh-CN/core/token-budget.md +108 -0
  64. package/bundled/locales/zh-CN/core/translation-lifecycle-standards.md +159 -0
  65. package/bundled/locales/zh-TW/CHANGELOG.md +51 -3
  66. package/bundled/locales/zh-TW/README.md +1 -1
  67. package/bundled/locales/zh-TW/core/anti-sycophancy-prompting.md +192 -0
  68. package/bundled/locales/zh-TW/core/capability-declaration.md +111 -0
  69. package/bundled/locales/zh-TW/core/circuit-breaker.md +111 -0
  70. package/bundled/locales/zh-TW/core/dual-phase-output.md +132 -0
  71. package/bundled/locales/zh-TW/core/failure-source-taxonomy.md +146 -0
  72. package/bundled/locales/zh-TW/core/frontend-design-standards.md +460 -0
  73. package/bundled/locales/zh-TW/core/health-check-standards.md +144 -0
  74. package/bundled/locales/zh-TW/core/immutability-first.md +159 -0
  75. package/bundled/locales/zh-TW/core/packaging-standards.md +224 -0
  76. package/bundled/locales/zh-TW/core/recovery-recipe-registry.md +146 -0
  77. package/bundled/locales/zh-TW/core/retry-standards.md +140 -0
  78. package/bundled/locales/zh-TW/core/security-decision.md +120 -0
  79. package/bundled/locales/zh-TW/core/skill-standard-alignment-check.md +112 -0
  80. package/bundled/locales/zh-TW/core/standard-admission-criteria.md +104 -0
  81. package/bundled/locales/zh-TW/core/standard-lifecycle-management.md +116 -0
  82. package/bundled/locales/zh-TW/core/timeout-standards.md +117 -0
  83. package/bundled/locales/zh-TW/core/token-budget.md +143 -0
  84. package/bundled/locales/zh-TW/core/translation-lifecycle-standards.md +159 -0
  85. package/bundled/skills/e2e-assistant/SKILL.md +19 -5
  86. package/bundled/skills/testing-guide/SKILL.md +5 -0
  87. package/bundled/skills/testing-guide/test-skeleton-templates.md +316 -0
  88. package/package.json +2 -1
  89. package/src/commands/check.js +6 -0
  90. package/src/commands/config.js +9 -0
  91. package/src/commands/init.js +97 -46
  92. package/src/commands/mcp.js +26 -0
  93. package/src/commands/run-intent.js +66 -0
  94. package/src/commands/update.js +41 -4
  95. package/src/core/command-router.js +85 -0
  96. package/src/core/project-config.js +91 -0
  97. package/src/flows/init-flow.js +6 -1
  98. package/src/i18n/messages.js +6 -6
  99. package/src/mcp/__tests__/server.test.js +251 -0
  100. package/src/mcp/server.js +352 -0
  101. package/src/prompts/init.js +157 -1
  102. package/src/reconciler/actual-state-scanner.js +24 -0
  103. package/src/uninstallers/hook-uninstaller.js +32 -1
  104. package/src/utils/detect-self-adoption.js +173 -0
  105. package/src/utils/e2e-analyzer.js +88 -5
  106. package/src/utils/e2e-detector.js +73 -1
  107. package/src/utils/integration-generator.js +22 -3
  108. package/standards-registry.json +203 -4
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Tests for UDS MCP Design Standards Server
3
+ * Uses vi.mock('fs') to avoid real filesystem I/O
4
+ */
5
+
6
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Mock 'fs' module — must be declared before importing the module under test
10
+ // ---------------------------------------------------------------------------
11
+ vi.mock('fs', () => ({
12
+ readFileSync: vi.fn(),
13
+ existsSync: vi.fn(),
14
+ }));
15
+
16
+ import { readFileSync, existsSync } from 'fs';
17
+ import { McpServer } from '../server.js';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Fixture data
21
+ // ---------------------------------------------------------------------------
22
+ const VALID_DESIGN_MD = `
23
+ # DESIGN.md — My Project
24
+
25
+ ## 1. Visual Theme & Mood
26
+ Theme: Minimal
27
+
28
+ ## 2. Color Palette
29
+ Primary: #0070f3
30
+
31
+ ## 3. Typography
32
+ Font: Inter
33
+
34
+ ## 4. Component Styling
35
+ Border-radius: 4px
36
+
37
+ ## 5. Layout & Spacing
38
+ Grid: 8px
39
+
40
+ ## 6. Design Guidelines
41
+ Keep it clean.
42
+
43
+ > **Version**: 1.0.0
44
+ `;
45
+
46
+ const INCOMPLETE_DESIGN_MD = `
47
+ # DESIGN.md — Incomplete Project
48
+
49
+ ## 1. Visual Theme & Mood
50
+ Theme: Dark
51
+ `;
52
+
53
+ const UDS_TEMPLATE_MD = `# DESIGN.md — [專案名稱] 前端設計規格
54
+ > **版本**: 1.0.0
55
+ `;
56
+
57
+ const FRONTEND_STANDARDS_YAML = `id: frontend-design-standards
58
+ meta:
59
+ version: "1.0.0"
60
+ `;
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Helpers
64
+ // ---------------------------------------------------------------------------
65
+ function makeServer() {
66
+ return new McpServer({
67
+ udsRoot: '/fake/project',
68
+ udsRepoRoot: '/fake/uds-repo',
69
+ });
70
+ }
71
+
72
+ function makeRequest(method, params = {}, id = 1) {
73
+ return { jsonrpc: '2.0', id, method, params };
74
+ }
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Tests
78
+ // ---------------------------------------------------------------------------
79
+ describe('McpServer', () => {
80
+ let server;
81
+
82
+ beforeEach(() => {
83
+ vi.resetAllMocks();
84
+ server = makeServer();
85
+ });
86
+
87
+ // -------------------------------------------------------------------------
88
+ // 1. handleInitialize — protocolVersion and serverInfo
89
+ // -------------------------------------------------------------------------
90
+ it('handleInitialize returns correct protocolVersion', () => {
91
+ const req = makeRequest('initialize');
92
+ const res = server.handleInitialize(req);
93
+ expect(res.result.protocolVersion).toBe('2024-11-05');
94
+ });
95
+
96
+ // -------------------------------------------------------------------------
97
+ // 2. handleInitialize — capabilities contains tools
98
+ // -------------------------------------------------------------------------
99
+ it('handleInitialize includes tools capability', () => {
100
+ const req = makeRequest('initialize');
101
+ const res = server.handleInitialize(req);
102
+ expect(res.result.capabilities).toHaveProperty('tools');
103
+ expect(res.result.serverInfo.name).toBe('uds-design-standards');
104
+ });
105
+
106
+ // -------------------------------------------------------------------------
107
+ // 3. handleToolsList — returns exactly 3 tools
108
+ // -------------------------------------------------------------------------
109
+ it('handleToolsList returns 3 tools', () => {
110
+ const req = makeRequest('tools/list');
111
+ const res = server.handleToolsList(req);
112
+ expect(res.result.tools).toHaveLength(3);
113
+ const names = res.result.tools.map((t) => t.name);
114
+ expect(names).toContain('get_design_token');
115
+ expect(names).toContain('get_design_standards');
116
+ expect(names).toContain('validate_design_token');
117
+ });
118
+
119
+ // -------------------------------------------------------------------------
120
+ // 4. handleToolsList — each tool has name, description, inputSchema
121
+ // -------------------------------------------------------------------------
122
+ it('handleToolsList tools each have name, description, inputSchema', () => {
123
+ const req = makeRequest('tools/list');
124
+ const res = server.handleToolsList(req);
125
+ for (const tool of res.result.tools) {
126
+ expect(tool).toHaveProperty('name');
127
+ expect(tool).toHaveProperty('description');
128
+ expect(tool).toHaveProperty('inputSchema');
129
+ }
130
+ });
131
+
132
+ // -------------------------------------------------------------------------
133
+ // 5. get_design_token — returns project DESIGN.md when it exists
134
+ // -------------------------------------------------------------------------
135
+ it('get_design_token returns project DESIGN.md content when file exists', () => {
136
+ existsSync.mockReturnValue(true);
137
+ readFileSync.mockReturnValue(VALID_DESIGN_MD);
138
+
139
+ const req = makeRequest('tools/call', {
140
+ name: 'get_design_token',
141
+ arguments: { project_path: '/some/project' },
142
+ });
143
+ const res = server.handleToolsCall(req);
144
+
145
+ expect(res.result.content[0].type).toBe('text');
146
+ expect(res.result.content[0].text).toContain('Visual Theme');
147
+ });
148
+
149
+ // -------------------------------------------------------------------------
150
+ // 6. get_design_token — returns UDS template when DESIGN.md not found
151
+ // -------------------------------------------------------------------------
152
+ it('get_design_token returns UDS template when DESIGN.md is missing', () => {
153
+ existsSync.mockReturnValue(false);
154
+ readFileSync.mockReturnValue(UDS_TEMPLATE_MD);
155
+
156
+ const req = makeRequest('tools/call', {
157
+ name: 'get_design_token',
158
+ arguments: { project_path: '/no/design/md/here' },
159
+ });
160
+ const res = server.handleToolsCall(req);
161
+
162
+ expect(res.result.content[0].text).toContain('UDS DESIGN.md template');
163
+ });
164
+
165
+ // -------------------------------------------------------------------------
166
+ // 7. get_design_token — returns content array with type "text"
167
+ // -------------------------------------------------------------------------
168
+ it('get_design_token returns content array with type text', () => {
169
+ existsSync.mockReturnValue(true);
170
+ readFileSync.mockReturnValue(VALID_DESIGN_MD);
171
+
172
+ const req = makeRequest('tools/call', {
173
+ name: 'get_design_token',
174
+ arguments: { project_path: '/some/project' },
175
+ });
176
+ const res = server.handleToolsCall(req);
177
+
178
+ expect(Array.isArray(res.result.content)).toBe(true);
179
+ expect(res.result.content[0]).toMatchObject({ type: 'text' });
180
+ });
181
+
182
+ // -------------------------------------------------------------------------
183
+ // 8. get_design_standards — returns frontend-design-standards.ai.yaml
184
+ // -------------------------------------------------------------------------
185
+ it('get_design_standards returns YAML standards content', () => {
186
+ readFileSync.mockReturnValue(FRONTEND_STANDARDS_YAML);
187
+
188
+ const req = makeRequest('tools/call', {
189
+ name: 'get_design_standards',
190
+ arguments: {},
191
+ });
192
+ const res = server.handleToolsCall(req);
193
+
194
+ expect(res.result.content[0].type).toBe('text');
195
+ expect(res.result.content[0].text).toContain('frontend-design-standards');
196
+ });
197
+
198
+ // -------------------------------------------------------------------------
199
+ // 9. validate_design_token — valid DESIGN.md returns valid=true, no missing
200
+ // -------------------------------------------------------------------------
201
+ it('validate_design_token returns valid=true for complete DESIGN.md', () => {
202
+ readFileSync.mockReturnValue(VALID_DESIGN_MD);
203
+
204
+ const req = makeRequest('tools/call', {
205
+ name: 'validate_design_token',
206
+ arguments: { design_md_path: '/some/project/DESIGN.md' },
207
+ });
208
+ const res = server.handleToolsCall(req);
209
+
210
+ const parsed = JSON.parse(res.result.content[0].text);
211
+ expect(parsed.valid).toBe(true);
212
+ expect(parsed.missing_sections).toHaveLength(0);
213
+ });
214
+
215
+ // -------------------------------------------------------------------------
216
+ // 10. validate_design_token — missing sections returns valid=false + list
217
+ // -------------------------------------------------------------------------
218
+ it('validate_design_token returns valid=false with missing sections listed', () => {
219
+ readFileSync.mockReturnValue(INCOMPLETE_DESIGN_MD);
220
+
221
+ const req = makeRequest('tools/call', {
222
+ name: 'validate_design_token',
223
+ arguments: { design_md_path: '/some/project/DESIGN.md' },
224
+ });
225
+ const res = server.handleToolsCall(req);
226
+
227
+ const parsed = JSON.parse(res.result.content[0].text);
228
+ expect(parsed.valid).toBe(false);
229
+ expect(parsed.missing_sections.length).toBeGreaterThan(0);
230
+ // color-palette should be missing
231
+ expect(parsed.missing_sections).toContain('color-palette');
232
+ });
233
+
234
+ // -------------------------------------------------------------------------
235
+ // 11. handleRequest — unknown method returns JSON-RPC error code -32601
236
+ // -------------------------------------------------------------------------
237
+ it('handleRequest returns -32601 for unknown method', () => {
238
+ const req = makeRequest('unknown/method');
239
+ const res = server.handleRequest(req);
240
+ expect(res.error.code).toBe(-32601);
241
+ });
242
+
243
+ // -------------------------------------------------------------------------
244
+ // 12. handleRequest — malformed request (non-object) returns error
245
+ // -------------------------------------------------------------------------
246
+ it('handleRequest handles malformed (non-object) request gracefully', () => {
247
+ const res = server.handleRequest(null);
248
+ expect(res).toHaveProperty('error');
249
+ expect(res.error.code).toBe(-32600);
250
+ });
251
+ });
@@ -0,0 +1,352 @@
1
+ /**
2
+ * UDS MCP Design Standards Server
3
+ * Implements MCP (Model Context Protocol) stdio transport using JSON-RPC 2.0
4
+ * No external MCP SDK required — uses Node.js standard library only
5
+ */
6
+
7
+ import { readFileSync, existsSync } from 'fs';
8
+ import { join, resolve, dirname } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+
13
+ // UDS standards root (relative to this file: cli/src/mcp/server.js → repo root is ../../../../)
14
+ const UDS_REPO_ROOT = resolve(__dirname, '../../../../');
15
+
16
+ /**
17
+ * Required DESIGN.md sections per frontend-design-standards.
18
+ * Each entry maps a section id (used in validation output) to a search pattern
19
+ * that should appear in the document (case-insensitive).
20
+ */
21
+ const REQUIRED_DESIGN_SECTIONS = [
22
+ { id: 'visual-theme', pattern: /visual[\s\-&]+theme/i },
23
+ { id: 'color-palette', pattern: /color[\s\-&]+palette/i },
24
+ { id: 'typography', pattern: /typography/i },
25
+ { id: 'component-styling', pattern: /component[\s\-&]+styling/i },
26
+ { id: 'layout-spacing', pattern: /layout[\s\S]{0,10}spacing/i },
27
+ { id: 'design-guidelines', pattern: /design[\s\S]{0,20}guidelines/i },
28
+ ];
29
+
30
+ export class McpServer {
31
+ constructor(options = {}) {
32
+ this.udsRoot = options.udsRoot || process.cwd();
33
+ // For reading UDS bundled files, always use UDS_REPO_ROOT
34
+ this.udsRepoRoot = options.udsRepoRoot || UDS_REPO_ROOT;
35
+ }
36
+
37
+ /**
38
+ * Start the MCP server — listens on stdin, writes to stdout
39
+ */
40
+ start() {
41
+ let buffer = '';
42
+
43
+ process.stdin.setEncoding('utf8');
44
+ process.stdin.on('data', (chunk) => {
45
+ buffer += chunk;
46
+ const lines = buffer.split('\n');
47
+ // Keep the last (potentially incomplete) line in buffer
48
+ buffer = lines.pop();
49
+
50
+ for (const line of lines) {
51
+ const trimmed = line.trim();
52
+ if (!trimmed) continue;
53
+
54
+ try {
55
+ const request = JSON.parse(trimmed);
56
+ const response = this.handleRequest(request);
57
+ if (response !== null && response !== undefined) {
58
+ process.stdout.write(JSON.stringify(response) + '\n');
59
+ }
60
+ } catch (e) {
61
+ // JSON parse error
62
+ const errorResponse = {
63
+ jsonrpc: '2.0',
64
+ id: null,
65
+ error: {
66
+ code: -32700,
67
+ message: 'Parse error',
68
+ data: e.message,
69
+ },
70
+ };
71
+ process.stdout.write(JSON.stringify(errorResponse) + '\n');
72
+ }
73
+ }
74
+ });
75
+
76
+ process.stdin.on('end', () => {
77
+ // Process any remaining buffered content
78
+ if (buffer.trim()) {
79
+ try {
80
+ const request = JSON.parse(buffer.trim());
81
+ const response = this.handleRequest(request);
82
+ if (response !== null && response !== undefined) {
83
+ process.stdout.write(JSON.stringify(response) + '\n');
84
+ }
85
+ } catch {
86
+ // Ignore final incomplete message
87
+ }
88
+ }
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Route a JSON-RPC 2.0 request to the appropriate handler
94
+ * @param {object} request
95
+ * @returns {object|null} JSON-RPC response (null for notifications)
96
+ */
97
+ handleRequest(request) {
98
+ if (!request || typeof request !== 'object') {
99
+ return {
100
+ jsonrpc: '2.0',
101
+ id: null,
102
+ error: { code: -32600, message: 'Invalid Request' },
103
+ };
104
+ }
105
+
106
+ const { method, id } = request;
107
+
108
+ // Notifications (no id) do not get a response
109
+ if (id === undefined) return null;
110
+
111
+ switch (method) {
112
+ case 'initialize':
113
+ return this.handleInitialize(request);
114
+ case 'tools/list':
115
+ return this.handleToolsList(request);
116
+ case 'tools/call':
117
+ return this.handleToolsCall(request);
118
+ default:
119
+ return {
120
+ jsonrpc: '2.0',
121
+ id,
122
+ error: {
123
+ code: -32601,
124
+ message: `Method not found: ${method}`,
125
+ },
126
+ };
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Handle MCP initialize request
132
+ */
133
+ handleInitialize(request) {
134
+ return {
135
+ jsonrpc: '2.0',
136
+ id: request.id,
137
+ result: {
138
+ protocolVersion: '2024-11-05',
139
+ capabilities: {
140
+ tools: {},
141
+ },
142
+ serverInfo: {
143
+ name: 'uds-design-standards',
144
+ version: '1.0.0',
145
+ },
146
+ },
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Handle tools/list request — return available tools
152
+ */
153
+ handleToolsList(request) {
154
+ return {
155
+ jsonrpc: '2.0',
156
+ id: request.id,
157
+ result: {
158
+ tools: [
159
+ {
160
+ name: 'get_design_token',
161
+ description:
162
+ 'Get the DESIGN.md content for a project, or return the UDS DESIGN.md template if not found',
163
+ inputSchema: {
164
+ type: 'object',
165
+ properties: {
166
+ project_path: {
167
+ type: 'string',
168
+ description: 'Absolute or relative path to the project root',
169
+ },
170
+ },
171
+ required: ['project_path'],
172
+ },
173
+ },
174
+ {
175
+ name: 'get_design_standards',
176
+ description:
177
+ 'Get the UDS frontend design standards (frontend-design-standards.ai.yaml)',
178
+ inputSchema: {
179
+ type: 'object',
180
+ properties: {},
181
+ },
182
+ },
183
+ {
184
+ name: 'validate_design_token',
185
+ description:
186
+ 'Validate a DESIGN.md file against UDS frontend design standards',
187
+ inputSchema: {
188
+ type: 'object',
189
+ properties: {
190
+ design_md_path: {
191
+ type: 'string',
192
+ description: 'Path to the DESIGN.md file to validate',
193
+ },
194
+ },
195
+ required: ['design_md_path'],
196
+ },
197
+ },
198
+ ],
199
+ },
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Handle tools/call request — dispatch to specific tool implementation
205
+ * @param {object} request
206
+ * @returns {object} JSON-RPC response
207
+ */
208
+ handleToolsCall(request) {
209
+ const { id, params } = request;
210
+ const toolName = params?.name;
211
+ const args = params?.arguments || {};
212
+
213
+ try {
214
+ let toolResult;
215
+
216
+ switch (toolName) {
217
+ case 'get_design_token':
218
+ toolResult = this._getDesignToken(args);
219
+ break;
220
+ case 'get_design_standards':
221
+ toolResult = this._getDesignStandards();
222
+ break;
223
+ case 'validate_design_token':
224
+ toolResult = this._validateDesignToken(args);
225
+ break;
226
+ default:
227
+ return {
228
+ jsonrpc: '2.0',
229
+ id,
230
+ error: {
231
+ code: -32601,
232
+ message: `Unknown tool: ${toolName}`,
233
+ },
234
+ };
235
+ }
236
+
237
+ return {
238
+ jsonrpc: '2.0',
239
+ id,
240
+ result: toolResult,
241
+ };
242
+ } catch (err) {
243
+ return {
244
+ jsonrpc: '2.0',
245
+ id,
246
+ error: {
247
+ code: -32000,
248
+ message: err.message,
249
+ },
250
+ };
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Tool: get_design_token
256
+ * Returns DESIGN.md content from the project, or UDS template if not found
257
+ */
258
+ _getDesignToken({ project_path }) {
259
+ const projectPath = resolve(project_path);
260
+ const designMdPath = join(projectPath, 'DESIGN.md');
261
+
262
+ let text;
263
+ let isTemplate = false;
264
+
265
+ if (existsSync(designMdPath)) {
266
+ text = readFileSync(designMdPath, 'utf8');
267
+ } else {
268
+ // Graceful fallback: return UDS template
269
+ const templatePath = join(this.udsRepoRoot, 'templates', 'DESIGN.md');
270
+ text = readFileSync(templatePath, 'utf8');
271
+ isTemplate = true;
272
+ }
273
+
274
+ const prefix = isTemplate
275
+ ? '<!-- This is the UDS DESIGN.md template. No DESIGN.md was found at the specified project path. -->\n\n'
276
+ : '';
277
+
278
+ return {
279
+ content: [
280
+ {
281
+ type: 'text',
282
+ text: prefix + text,
283
+ },
284
+ ],
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Tool: get_design_standards
290
+ * Returns the UDS frontend-design-standards.ai.yaml content
291
+ */
292
+ _getDesignStandards() {
293
+ const standardsPath = join(
294
+ this.udsRepoRoot,
295
+ 'ai',
296
+ 'standards',
297
+ 'frontend-design-standards.ai.yaml'
298
+ );
299
+ const text = readFileSync(standardsPath, 'utf8');
300
+
301
+ return {
302
+ content: [
303
+ {
304
+ type: 'text',
305
+ text,
306
+ },
307
+ ],
308
+ };
309
+ }
310
+
311
+ /**
312
+ * Tool: validate_design_token
313
+ * Validates a DESIGN.md file against required sections
314
+ */
315
+ _validateDesignToken({ design_md_path }) {
316
+ const filePath = resolve(design_md_path);
317
+ const content = readFileSync(filePath, 'utf8');
318
+
319
+ const missingSections = [];
320
+ const warnings = [];
321
+
322
+ for (const section of REQUIRED_DESIGN_SECTIONS) {
323
+ if (!section.pattern.test(content)) {
324
+ missingSections.push(section.id);
325
+ }
326
+ }
327
+
328
+ // Optional: warn if no version info found
329
+ if (!/version/i.test(content)) {
330
+ warnings.push('No version information found in DESIGN.md');
331
+ }
332
+
333
+ const valid = missingSections.length === 0;
334
+
335
+ return {
336
+ content: [
337
+ {
338
+ type: 'text',
339
+ text: JSON.stringify(
340
+ {
341
+ valid,
342
+ missing_sections: missingSections,
343
+ warnings,
344
+ },
345
+ null,
346
+ 2
347
+ ),
348
+ },
349
+ ],
350
+ };
351
+ }
352
+ }