@wendongfly/myhi 1.3.54 → 1.3.56

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.
@@ -0,0 +1,14 @@
1
+ # E2E 端到端测试(Playwright)
2
+
3
+ 使用 Playwright 对指定功能或页面进行自动化端到端测试。
4
+
5
+ 执行步骤:
6
+ 1. 确认 Playwright 已安装,若未安装先完成安装(检查 @playwright/test + 浏览器)
7
+ 2. 了解测试目标:应用 URL、模块名称、核心用户流程
8
+ 3. 分析页面结构(优先用 getByRole / getByLabel / getByText 定位,避免脆弱 CSS 选择器)
9
+ 4. 编写测试用例,覆盖:主流程(happy path)+ 关键边界(表单校验、权限拦截、空状态)
10
+ 5. 运行测试:npx playwright test --reporter=list
11
+ 6. 失败时:查看截图和 trace,分析根因,修复后重跑
12
+ 7. 输出测试报告:通过/失败数量 + 覆盖的功能点列表
13
+
14
+ 目标模块或说明:$ARGUMENTS
@@ -0,0 +1,14 @@
1
+ # 检查并安装 Playwright
2
+
3
+ 检测当前项目的 Playwright 安装状态,按需完成安装和配置。
4
+
5
+ 执行步骤:
6
+ 1. 识别包管理器(检查 pnpm-lock.yaml / yarn.lock / package-lock.json)
7
+ 2. 检查 `@playwright/test` 是否已在 devDependencies 中
8
+ 3. 若未安装:用检测到的包管理器安装 `@playwright/test`
9
+ 4. 检查浏览器:运行 `npx playwright install --dry-run`,若缺少则安装 chromium
10
+ 5. 检查是否有 `playwright.config.{ts,js}`,若无则根据项目框架(Vite/Next/Nuxt 等)生成合适的配置
11
+ 6. 验证安装:运行一个最简示例测试确认环境正常
12
+ 7. 输出可用的测试命令清单
13
+
14
+ $ARGUMENTS
@@ -0,0 +1,21 @@
1
+ # 插入拟真测试数据
2
+
3
+ 通过 Playwright UI 操作或直接 API 调用,为指定模块批量写入符合真实场景的测试数据。
4
+
5
+ 数据生成规范(中国用户习惯):
6
+ - 姓名:常用汉字姓氏 + 名(2-3字,自然感强)
7
+ - 手机:1xx 开头,用 130-139 / 150-159 / 180-189 等真实号段
8
+ - 邮箱:拼音+数字 @ qq.com / 163.com / gmail.com
9
+ - 地址:省市区+街道+门牌,层级完整真实
10
+ - 身份证:区域码正确+真实生日+合法顺序码(仅测试用途)
11
+ - 金额:符合业务量级(几十到几千元,非整数)
12
+ - 日期:最近 1 年内随机,格式与系统保持一致
13
+
14
+ 执行步骤:
15
+ 1. 分析目标模块的字段要求(查看表单 / API 接口 / 数据库 schema)
16
+ 2. 生成 15-20 条完整拟真记录(多样性:不同年龄段、城市、金额区间)
17
+ 3. 选择最高效写入方式:API 直调(首选)→ Playwright UI 填表 → 直接 SQL
18
+ 4. 批量执行写入,遇错记录并继续(不中断整批)
19
+ 5. 验证:查询条数与关键字段,确认数据已成功入库
20
+
21
+ 目标模块:$ARGUMENTS
package/dist/chat.html CHANGED
@@ -350,6 +350,7 @@
350
350
  <button class="sk sk-claude" onclick="openGitSheet()" style="color:#3fb950">提交</button>
351
351
  <button class="sk sk-claude" onclick="showMemory()">记忆</button>
352
352
  <button class="sk sk-claude" onclick="openSkillSheet()" style="color:#9d5cf5">技能</button>
353
+ <span id="sk-builtin-skills"></span>
353
354
  <span id="sk-custom-skills"></span>
354
355
  </div>
355
356
  </div>
@@ -2403,16 +2404,23 @@
2403
2404
  try {
2404
2405
  const resp = await fetch(`/api/skills?sessionId=${SESSION_ID}`);
2405
2406
  const data = await resp.json();
2406
- const container = document.getElementById('sk-custom-skills');
2407
2407
  _customSkills = data.skills || [];
2408
- if (!_customSkills.length) { container.innerHTML = ''; return; }
2409
- // ≤3 个:直接显示按钮;>3 个:显示 "/命令 (N)" 入口
2410
- if (_customSkills.length <= 3) {
2411
- container.innerHTML = _customSkills.map(s =>
2408
+ const builtins = _customSkills.filter(s => s.source === 'builtin');
2409
+ const customs = _customSkills.filter(s => s.source !== 'builtin');
2410
+ // 内置技能:始终逐个显示(青绿色)
2411
+ const builtinEl = document.getElementById('sk-builtin-skills');
2412
+ builtinEl.innerHTML = builtins.map(s =>
2413
+ `<button class="sk sk-claude" onclick="runSkill('${escHtml(s.name)}')" title="${escHtml(s.desc)}" style="color:#26a69a">/${escHtml(s.name)}</button>`
2414
+ ).join('');
2415
+ // 用户/项目技能:≤3 显示按钮;>3 折叠
2416
+ const customEl = document.getElementById('sk-custom-skills');
2417
+ if (!customs.length) { customEl.innerHTML = ''; return; }
2418
+ if (customs.length <= 3) {
2419
+ customEl.innerHTML = customs.map(s =>
2412
2420
  `<button class="sk sk-claude" onclick="runSkill('${escHtml(s.name)}')" title="${escHtml(s.desc)}" style="color:#9d5cf5">/${escHtml(s.name)}</button>`
2413
2421
  ).join('');
2414
2422
  } else {
2415
- container.innerHTML = `<button class="sk sk-claude" onclick="openCmdSheet()" style="color:#9d5cf5">/命令 (${_customSkills.length})</button>`;
2423
+ customEl.innerHTML = `<button class="sk sk-claude" onclick="openCmdSheet()" style="color:#9d5cf5">/命令 (${customs.length})</button>`;
2416
2424
  }
2417
2425
  } catch {}
2418
2426
  }
@@ -2444,15 +2452,33 @@
2444
2452
  document.getElementById('skill-run-sheet').classList.remove('open');
2445
2453
  _pendingSkillName = null;
2446
2454
  };
2447
- window.submitSkillRun = function() {
2455
+ window.submitSkillRun = async function() {
2448
2456
  if (!_pendingSkillName) return;
2449
2457
  const args = document.getElementById('skill-run-input').value.trim();
2450
2458
  const name = _pendingSkillName;
2459
+ const skill = _customSkills.find(s => s.name === name);
2451
2460
  closeSkillRunSheet();
2452
2461
  if (!isController && canTakeControl()) socket.emit('take-control', { sessionId: SESSION_ID });
2453
- const cmd = args ? `/${name} ${args}` : `/${name}`;
2454
- socket.emit('agent:query', { prompt: cmd });
2455
- addInputMessage(cmd);
2462
+ const displayCmd = args ? `/${name} ${args}` : `/${name}`;
2463
+ if (skill?.source === 'builtin') {
2464
+ // 内置技能不在 .claude/commands/ 里,需拉取内容后直接发送为 prompt
2465
+ try {
2466
+ const resp = await fetch(`/api/skills/content?name=${encodeURIComponent(name)}&sessionId=${SESSION_ID}`);
2467
+ const data = await resp.json();
2468
+ let prompt = data.content || displayCmd;
2469
+ if (args) {
2470
+ prompt = prompt.replace(/\$ARGUMENTS/g, args);
2471
+ } else {
2472
+ prompt = prompt.replace(/[^\n]*\$ARGUMENTS[^\n]*/g, '').replace(/\n{3,}/g, '\n\n').trim();
2473
+ }
2474
+ socket.emit('agent:query', { prompt });
2475
+ } catch {
2476
+ socket.emit('agent:query', { prompt: displayCmd });
2477
+ }
2478
+ } else {
2479
+ socket.emit('agent:query', { prompt: displayCmd });
2480
+ }
2481
+ addInputMessage(displayCmd);
2456
2482
  showThinking();
2457
2483
  };
2458
2484
  window.openCmdSheet = function() {
@@ -2462,8 +2488,9 @@
2462
2488
  filterInp.value = '';
2463
2489
  const render = (keyword = '') => {
2464
2490
  const kw = keyword.toLowerCase();
2491
+ // 命令面板只列用户/项目技能(内置技能始终在快捷栏直接显示)
2465
2492
  const filtered = _customSkills.filter(s =>
2466
- !kw || s.name.toLowerCase().includes(kw) || (s.desc || '').toLowerCase().includes(kw));
2493
+ s.source !== 'builtin' && (!kw || s.name.toLowerCase().includes(kw) || (s.desc || '').toLowerCase().includes(kw)));
2467
2494
  listEl.innerHTML = filtered.length
2468
2495
  ? filtered.map(s => `<div class="action-sheet-item" onclick="runSkillAndClose('${escHtml(s.name)}')">
2469
2496
  <div><div style="color:#d2a8ff">/${escHtml(s.name)}</div>${s.desc ? `<div class="desc">${escHtml(s.desc)}</div>` : ''}</div>
@@ -2505,11 +2532,12 @@
2505
2532
  try {
2506
2533
  const resp = await fetch(`/api/skills?sessionId=${SESSION_ID}&detail=1`);
2507
2534
  const data = await resp.json();
2508
- if (!data.skills || !data.skills.length) {
2535
+ const editable = (data.skills || []).filter(s => s.source !== 'builtin');
2536
+ if (!editable.length) {
2509
2537
  listEl.innerHTML = '<div style="text-align:center;color:#8b949e;padding:2rem 0">暂无技能,点击右上角 + 新建</div>';
2510
2538
  return;
2511
2539
  }
2512
- listEl.innerHTML = data.skills.map(s => `
2540
+ listEl.innerHTML = editable.map(s => `
2513
2541
  <div class="action-sheet-item" style="gap:0.5rem">
2514
2542
  <div style="min-width:0;flex:1;cursor:pointer" onclick="editSkill('${escHtml(s.name)}','${escHtml(s.source)}')">
2515
2543
  <div style="color:#9d5cf5;font-weight:500">/${escHtml(s.name)}</div>