mcp-probe-kit 3.0.3 → 3.0.6
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/README.md +457 -423
- package/build/index.js +434 -140
- package/build/lib/template-loader.js +317 -317
- package/build/lib/tool-execution-context.d.ts +8 -0
- package/build/lib/tool-execution-context.js +20 -0
- package/build/schemas/git-tools.js +16 -16
- package/build/tools/__tests__/start_bugfix.unit.test.js +14 -14
- package/build/tools/__tests__/start_ui.unit.test.js +11 -11
- package/build/tools/add_feature.js +79 -79
- package/build/tools/ask_user.js +5 -5
- package/build/tools/interview.js +9 -9
- package/build/tools/start_bugfix.d.ts +2 -1
- package/build/tools/start_bugfix.js +131 -122
- package/build/tools/start_feature.d.ts +2 -1
- package/build/tools/start_feature.js +113 -104
- package/build/tools/start_onboard.d.ts +2 -1
- package/build/tools/start_onboard.js +57 -51
- package/build/tools/start_product.d.ts +2 -1
- package/build/tools/start_product.js +9 -1
- package/build/tools/start_ralph.d.ts +2 -1
- package/build/tools/start_ralph.js +9 -3
- package/build/tools/start_ui.d.ts +2 -1
- package/build/tools/start_ui.js +102 -88
- package/build/tools/ui-ux-tools.d.ts +2 -1
- package/build/tools/ui-ux-tools.js +19 -3
- package/build/utils/ui-sync.d.ts +6 -2
- package/build/utils/ui-sync.js +125 -29
- package/docs/assets/font/MaterialSymbolsOutlined.codepoints +4102 -0
- package/docs/assets/font/MaterialSymbolsOutlined.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-400.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-700.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-900.ttf +0 -0
- package/docs/assets/js/i18n.js +375 -0
- package/docs/assets/js/tailwind.js +83 -83
- package/docs/assets/logo-zh.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/data/tools.js +21 -21
- package/docs/debug-i18n.html +163 -0
- package/docs/i18n/all-tools/en.json +157 -0
- package/docs/i18n/all-tools/ja.json +157 -0
- package/docs/i18n/all-tools/ko.json +157 -0
- package/docs/i18n/all-tools/zh-CN.json +157 -0
- package/docs/i18n/en.json +518 -0
- package/docs/i18n/ja.json +518 -0
- package/docs/i18n/ko.json +518 -0
- package/docs/i18n/zh-CN.json +518 -0
- package/docs/index.html +43 -32
- package/docs/pages/all-tools.html +514 -330
- package/docs/pages/examples.html +689 -673
- package/docs/pages/getting-started.html +589 -577
- package/docs/pages/migration.html +298 -283
- package/package.json +6 -6
- package/docs/project-context/architecture.md +0 -0
- package/docs/project-context/how-to-develop.md +0 -313
- package/docs/project-context/how-to-test.md +0 -457
- package/docs/project-context/tech-stack.md +0 -96
- package/docs/project-context.md +0 -53
- package/docs/specs/git-work-report/design.md +0 -568
- package/docs/specs/git-work-report/requirements.md +0 -131
- package/docs/specs/git-work-report/tasks.md +0 -197
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 客户端国际化系统
|
|
3
|
+
* 直接在浏览器中切换语言,无需生成多个文件
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 支持的语言
|
|
7
|
+
const LANGUAGES = {
|
|
8
|
+
'zh-CN': { name: '中文', flag: '🇨🇳' },
|
|
9
|
+
'en': { name: 'English', flag: '🇺🇸' },
|
|
10
|
+
'ja': { name: '日本語', flag: '🇯🇵' },
|
|
11
|
+
'ko': { name: '한국어', flag: '🇰🇷' },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// 当前语言
|
|
15
|
+
let currentLang = localStorage.getItem('lang') || 'zh-CN';
|
|
16
|
+
|
|
17
|
+
// 翻译数据缓存
|
|
18
|
+
const translations = {};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 加载翻译文件
|
|
22
|
+
*/
|
|
23
|
+
async function loadTranslations(lang) {
|
|
24
|
+
if (translations[lang]) {
|
|
25
|
+
return translations[lang];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// 根据当前页面路径确定翻译文件路径
|
|
30
|
+
const isInPages = window.location.pathname.includes('/pages/');
|
|
31
|
+
const isAllToolsPage = window.location.pathname.includes('all-tools.html');
|
|
32
|
+
|
|
33
|
+
let basePath = isInPages ? '../i18n' : './i18n';
|
|
34
|
+
|
|
35
|
+
// 如果是 all-tools 页面,需要加载两个翻译文件
|
|
36
|
+
if (isAllToolsPage) {
|
|
37
|
+
// 1. 先加载通用翻译(侧边栏等)
|
|
38
|
+
const commonPath = `${basePath}/${lang}.json`;
|
|
39
|
+
console.log(`[i18n] Loading common translation: ${lang}, path: ${commonPath}`);
|
|
40
|
+
|
|
41
|
+
let commonTranslations = {};
|
|
42
|
+
try {
|
|
43
|
+
const commonResponse = await fetch(commonPath);
|
|
44
|
+
if (commonResponse.ok) {
|
|
45
|
+
commonTranslations = await commonResponse.json();
|
|
46
|
+
console.log(`[i18n] Common translation loaded successfully:`, lang);
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.warn(`通用翻译文件加载失败: ${commonPath}`, error);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 2. 再加载页面特定翻译(工具数据)
|
|
53
|
+
const toolsPath = `${basePath}/all-tools/${lang}.json`;
|
|
54
|
+
console.log(`[i18n] Loading all-tools translation: ${lang}, path: ${toolsPath}`);
|
|
55
|
+
|
|
56
|
+
const toolsResponse = await fetch(toolsPath);
|
|
57
|
+
if (!toolsResponse.ok) {
|
|
58
|
+
console.warn(`工具翻译文件不存在: ${toolsPath},回退到中文`);
|
|
59
|
+
// 回退到中文
|
|
60
|
+
if (lang !== 'zh-CN') {
|
|
61
|
+
const fallbackCommonPath = `${basePath}/zh-CN.json`;
|
|
62
|
+
const fallbackToolsPath = `${basePath}/all-tools/zh-CN.json`;
|
|
63
|
+
|
|
64
|
+
const [fallbackCommonRes, fallbackToolsRes] = await Promise.all([
|
|
65
|
+
fetch(fallbackCommonPath),
|
|
66
|
+
fetch(fallbackToolsPath)
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
if (fallbackCommonRes.ok && fallbackToolsRes.ok) {
|
|
70
|
+
const fallbackCommon = await fallbackCommonRes.json();
|
|
71
|
+
const fallbackTools = await fallbackToolsRes.json();
|
|
72
|
+
translations[lang] = { ...fallbackCommon, ...fallbackTools };
|
|
73
|
+
return translations[lang];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const toolsTranslations = await toolsResponse.json();
|
|
80
|
+
|
|
81
|
+
console.log(`[i18n] Tools translations loaded, checking toolShortDesc...`);
|
|
82
|
+
console.log(`[i18n] toolsTranslations keys:`, Object.keys(toolsTranslations));
|
|
83
|
+
console.log(`[i18n] toolsTranslations full content:`, JSON.stringify(toolsTranslations, null, 2).substring(0, 500));
|
|
84
|
+
console.log(`[i18n] toolsTranslations.toolShortDesc exists:`, !!toolsTranslations.toolShortDesc);
|
|
85
|
+
console.log(`[i18n] toolsTranslations.toolShortDesc content:`, toolsTranslations.toolShortDesc);
|
|
86
|
+
|
|
87
|
+
// 3. 合并两个翻译对象
|
|
88
|
+
// 使用深度合并:commonTranslations 作为基础,toolsTranslations 覆盖同名键
|
|
89
|
+
// 但保留 toolsTranslations 中独有的键(如 toolShortDesc)
|
|
90
|
+
translations[lang] = {
|
|
91
|
+
...commonTranslations,
|
|
92
|
+
...toolsTranslations,
|
|
93
|
+
// 确保 toolShortDesc 不会被覆盖
|
|
94
|
+
toolShortDesc: toolsTranslations.toolShortDesc || {}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
console.log(`[i18n] All-tools translation loaded and merged successfully:`, lang);
|
|
98
|
+
console.log(`[i18n] Common translations keys:`, Object.keys(commonTranslations));
|
|
99
|
+
console.log(`[i18n] Tools translations keys:`, Object.keys(toolsTranslations));
|
|
100
|
+
console.log(`[i18n] Merged translations keys:`, Object.keys(translations[lang]));
|
|
101
|
+
console.log(`[i18n] toolShortDesc in tools:`, !!toolsTranslations.toolShortDesc);
|
|
102
|
+
console.log(`[i18n] toolShortDesc in merged:`, !!translations[lang].toolShortDesc);
|
|
103
|
+
if (translations[lang].toolShortDesc) {
|
|
104
|
+
console.log(`[i18n] toolShortDesc content:`, translations[lang].toolShortDesc);
|
|
105
|
+
}
|
|
106
|
+
return translations[lang];
|
|
107
|
+
} else {
|
|
108
|
+
// 其他页面使用通用翻译文件
|
|
109
|
+
const translationPath = `${basePath}/${lang}.json`;
|
|
110
|
+
console.log(`[i18n] Loading translation: ${lang}, path: ${translationPath}`);
|
|
111
|
+
|
|
112
|
+
const response = await fetch(translationPath);
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
console.warn(`翻译文件不存在: ${translationPath}`);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
translations[lang] = await response.json();
|
|
118
|
+
console.log(`[i18n] Translation loaded successfully:`, lang);
|
|
119
|
+
return translations[lang];
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(`加载翻译失败: ${lang}`, error);
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 翻译文本
|
|
129
|
+
*/
|
|
130
|
+
function t(key, lang = currentLang) {
|
|
131
|
+
const trans = translations[lang];
|
|
132
|
+
if (!trans) return key;
|
|
133
|
+
|
|
134
|
+
const keys = key.split('.');
|
|
135
|
+
let value = trans;
|
|
136
|
+
|
|
137
|
+
for (const k of keys) {
|
|
138
|
+
if (value && typeof value === 'object' && k in value) {
|
|
139
|
+
value = value[k];
|
|
140
|
+
} else {
|
|
141
|
+
return key;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 应用翻译到页面
|
|
150
|
+
*/
|
|
151
|
+
function applyTranslations() {
|
|
152
|
+
// 翻译所有带 data-i18n 属性的元素
|
|
153
|
+
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
154
|
+
const key = el.getAttribute('data-i18n');
|
|
155
|
+
const text = t(key);
|
|
156
|
+
if (text !== key) {
|
|
157
|
+
el.textContent = text;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// 翻译所有带 data-i18n-placeholder 属性的元素
|
|
162
|
+
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
|
163
|
+
const key = el.getAttribute('data-i18n-placeholder');
|
|
164
|
+
const text = t(key);
|
|
165
|
+
if (text !== key) {
|
|
166
|
+
el.placeholder = text;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// 翻译所有带 data-i18n-title 属性的元素
|
|
171
|
+
document.querySelectorAll('[data-i18n-title]').forEach(el => {
|
|
172
|
+
const key = el.getAttribute('data-i18n-title');
|
|
173
|
+
const text = t(key);
|
|
174
|
+
if (text !== key) {
|
|
175
|
+
el.title = text;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// 更新 HTML lang 属性
|
|
180
|
+
document.documentElement.lang = currentLang;
|
|
181
|
+
|
|
182
|
+
// 更新页面标题
|
|
183
|
+
const titleKey = document.querySelector('meta[name="i18n-title"]')?.content;
|
|
184
|
+
if (titleKey) {
|
|
185
|
+
const title = t(titleKey);
|
|
186
|
+
if (title !== titleKey) {
|
|
187
|
+
document.title = title;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 标记 i18n 已准备好,显示页面内容
|
|
192
|
+
document.body.classList.add('i18n-ready');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 切换语言
|
|
197
|
+
*/
|
|
198
|
+
async function switchLanguage(lang) {
|
|
199
|
+
if (!LANGUAGES[lang]) {
|
|
200
|
+
console.error(`不支持的语言: ${lang}`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
currentLang = lang;
|
|
205
|
+
window.currentLang = lang; // 同步更新全局变量
|
|
206
|
+
localStorage.setItem('lang', lang);
|
|
207
|
+
|
|
208
|
+
// 加载翻译
|
|
209
|
+
await loadTranslations(lang);
|
|
210
|
+
|
|
211
|
+
// 应用翻译
|
|
212
|
+
applyTranslations();
|
|
213
|
+
|
|
214
|
+
// 更新语言切换器
|
|
215
|
+
updateLanguageSwitcher();
|
|
216
|
+
|
|
217
|
+
// 关闭菜单
|
|
218
|
+
const menu = document.getElementById('lang-menu');
|
|
219
|
+
const icon = document.getElementById('lang-icon');
|
|
220
|
+
if (menu && icon) {
|
|
221
|
+
menu.classList.add('hidden');
|
|
222
|
+
icon.style.transform = 'rotate(0deg)';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// 触发自定义事件
|
|
226
|
+
window.dispatchEvent(new CustomEvent('languageChanged', { detail: { lang } }));
|
|
227
|
+
document.dispatchEvent(new CustomEvent('languageChanged', { detail: { lang } }));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 创建语言切换器
|
|
232
|
+
*/
|
|
233
|
+
function createLanguageSwitcher() {
|
|
234
|
+
const container = document.getElementById('lang-switcher-container');
|
|
235
|
+
if (!container) {
|
|
236
|
+
console.warn('语言切换器容器不存在: #lang-switcher-container');
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
console.log('创建语言切换器...');
|
|
241
|
+
|
|
242
|
+
const currentLangInfo = LANGUAGES[currentLang];
|
|
243
|
+
|
|
244
|
+
const html = `
|
|
245
|
+
<div class="relative inline-block" id="lang-switcher">
|
|
246
|
+
<button onclick="toggleLangMenu()" class="flex items-center gap-1 px-3 py-2 text-sm text-text-secondary hover:text-primary transition-colors rounded-md hover:bg-bg-hover">
|
|
247
|
+
<span id="current-lang-display">${currentLangInfo.flag} ${currentLangInfo.name}</span>
|
|
248
|
+
<svg class="w-4 h-4 transition-transform" id="lang-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
249
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
250
|
+
</svg>
|
|
251
|
+
</button>
|
|
252
|
+
<div id="lang-menu" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-border py-1 z-50">
|
|
253
|
+
${Object.entries(LANGUAGES).map(([code, info]) => `
|
|
254
|
+
<a href="#" onclick="switchLanguage('${code}'); return false;"
|
|
255
|
+
class="block px-4 py-2 text-sm hover:bg-bg-hover transition-colors ${code === currentLang ? 'bg-blue-50 text-primary font-medium' : ''}">
|
|
256
|
+
${info.flag} ${info.name}
|
|
257
|
+
</a>
|
|
258
|
+
`).join('')}
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
container.innerHTML = html;
|
|
264
|
+
console.log('语言切换器创建完成');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 更新语言切换器显示
|
|
269
|
+
*/
|
|
270
|
+
function updateLanguageSwitcher() {
|
|
271
|
+
const display = document.getElementById('current-lang-display');
|
|
272
|
+
if (display) {
|
|
273
|
+
const info = LANGUAGES[currentLang];
|
|
274
|
+
display.textContent = `${info.flag} ${info.name}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 更新选中状态
|
|
278
|
+
document.querySelectorAll('#lang-menu a').forEach(link => {
|
|
279
|
+
const lang = link.getAttribute('onclick').match(/'([^']+)'/)[1];
|
|
280
|
+
if (lang === currentLang) {
|
|
281
|
+
link.classList.add('bg-blue-50', 'text-primary', 'font-medium');
|
|
282
|
+
} else {
|
|
283
|
+
link.classList.remove('bg-blue-50', 'text-primary', 'font-medium');
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* 切换语言菜单
|
|
290
|
+
*/
|
|
291
|
+
function toggleLangMenu() {
|
|
292
|
+
const menu = document.getElementById('lang-menu');
|
|
293
|
+
const icon = document.getElementById('lang-icon');
|
|
294
|
+
|
|
295
|
+
if (menu.classList.contains('hidden')) {
|
|
296
|
+
menu.classList.remove('hidden');
|
|
297
|
+
icon.style.transform = 'rotate(180deg)';
|
|
298
|
+
} else {
|
|
299
|
+
menu.classList.add('hidden');
|
|
300
|
+
icon.style.transform = 'rotate(0deg)';
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 点击外部关闭菜单
|
|
306
|
+
*/
|
|
307
|
+
document.addEventListener('click', (e) => {
|
|
308
|
+
const switcher = document.getElementById('lang-switcher');
|
|
309
|
+
if (switcher && !switcher.contains(e.target)) {
|
|
310
|
+
const menu = document.getElementById('lang-menu');
|
|
311
|
+
const icon = document.getElementById('lang-icon');
|
|
312
|
+
if (menu && !menu.classList.contains('hidden')) {
|
|
313
|
+
menu.classList.add('hidden');
|
|
314
|
+
icon.style.transform = 'rotate(0deg)';
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 初始化
|
|
321
|
+
*/
|
|
322
|
+
async function initI18n() {
|
|
323
|
+
// 从 localStorage 读取保存的语言,如果没有则检测浏览器语言
|
|
324
|
+
const savedLang = localStorage.getItem('lang');
|
|
325
|
+
|
|
326
|
+
if (savedLang && LANGUAGES[savedLang]) {
|
|
327
|
+
// 使用保存的语言
|
|
328
|
+
currentLang = savedLang;
|
|
329
|
+
console.log('[i18n] Using saved language:', currentLang);
|
|
330
|
+
} else {
|
|
331
|
+
// 检测浏览器语言(仅首次访问)
|
|
332
|
+
const browserLang = navigator.language.toLowerCase();
|
|
333
|
+
if (browserLang.startsWith('zh')) currentLang = 'zh-CN';
|
|
334
|
+
else if (browserLang.startsWith('ja')) currentLang = 'ja';
|
|
335
|
+
else if (browserLang.startsWith('ko')) currentLang = 'ko';
|
|
336
|
+
else currentLang = 'en'; // 默认英文
|
|
337
|
+
|
|
338
|
+
localStorage.setItem('lang', currentLang);
|
|
339
|
+
console.log('[i18n] Detected browser language:', currentLang);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 同步到全局变量
|
|
343
|
+
window.currentLang = currentLang;
|
|
344
|
+
|
|
345
|
+
// 加载当前语言的翻译
|
|
346
|
+
await loadTranslations(currentLang);
|
|
347
|
+
|
|
348
|
+
// 创建语言切换器
|
|
349
|
+
createLanguageSwitcher();
|
|
350
|
+
|
|
351
|
+
// 应用翻译
|
|
352
|
+
applyTranslations();
|
|
353
|
+
|
|
354
|
+
// 触发 i18n 准备完成事件
|
|
355
|
+
console.log('[i18n] i18n initialization completed, dispatching i18nReady event');
|
|
356
|
+
window.dispatchEvent(new CustomEvent('i18nReady', { detail: { lang: currentLang } }));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// 页面加载完成后初始化
|
|
360
|
+
if (document.readyState === 'loading') {
|
|
361
|
+
document.addEventListener('DOMContentLoaded', initI18n);
|
|
362
|
+
} else {
|
|
363
|
+
initI18n();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// 导出到全局
|
|
367
|
+
window.switchLanguage = switchLanguage;
|
|
368
|
+
window.toggleLangMenu = toggleLangMenu;
|
|
369
|
+
window.t = t;
|
|
370
|
+
window.currentLang = currentLang;
|
|
371
|
+
window.translations = translations;
|
|
372
|
+
window.LANGUAGES = LANGUAGES;
|
|
373
|
+
window.applyTranslations = applyTranslations;
|
|
374
|
+
|
|
375
|
+
|