Skip to content

feat(memory): add autoSkill background project skill extraction#3673

Merged
LaZzyMan merged 13 commits into
mainfrom
feat/self-improvement
May 9, 2026
Merged

feat(memory): add autoSkill background project skill extraction#3673
LaZzyMan merged 13 commits into
mainfrom
feat/self-improvement

Conversation

@LaZzyMan

@LaZzyMan LaZzyMan commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator

TLDR

新增 autoSkill 功能:当会话内工具调用次数达到阈值(默认 20 次)时,在会话结束后自动 fork 一个后台 review agent,将本次对话中发现的可复用操作流程提炼为 project-level skill,写入 ${projectRoot}/.qwen/skills/

该功能默认关闭(memory.enableAutoSkill: false),用户需在 .qwen/settings.json 中显式开启。

核心变更:

  • 删除 skill_manage 专用工具,review agent 改用通用 write_file / edit,写保护改由双层机制实现(直接响应 reviewer 的安全反馈)
  • skillsModifiedInSession 路径检测recordCompletedToolCall(name, filePath?) 检测每次工具调用的 file_path 参数,凡写入 .qwen/skills/ 的操作均将 skillsModifiedInSession 置为 true,会话结束时跳过 review 调度(防止 review agent 和用户在同一会话内双写冲突)
  • source: auto-skill frontmatter 双重保护:review agent 权限沙箱在允许 EDIT/WRITE_FILE 前,同时验证目标路径在 skills 目录内文件已含 source: auto-skill frontmatter,防止 agent 覆盖用户手写技能
  • 新增 skillReviewAgentPlanner.ts:构建 skill review agent 的 prompt 和权限沙箱
  • 配置从 skillNudge.*(4 个字段)简化为 memory.enableAutoSkill(单一开关),阈值/轮次/超时全部硬编码

Screenshots / Video Demo

N/A — CLI 后台任务,无直接 UI 变化。效果体现在会话结束后 .qwen/skills/ 目录内新增 SKILL.md 文件。

$ ls .qwen/skills/
read-file-text-params/

$ cat .qwen/skills/read-file-text-params/SKILL.md
---
name: read-file-text-params
description: 读取文件内容并转换为 text/plain 格式
source: auto-skill
extracted_at: 2025-07-10T12:34:56.789Z
---

Dive Deeper

设计文档见 docs/design/skill-nudge/skill-nudge.md

架构决策:

  1. 删除 skill_manage,双层写保护取代
    • 层 1(session 层):client.tsrecordCompletedToolCall 检测 file_path 是否指向 .qwen/skills/,有写操作则置 skillsModifiedInSession=true,会话结束不再触发 review
    • 层 2(permission sandbox 层):skillReviewAgentPlanner.tsevaluateScopedDecision 对 EDIT/WRITE_FILE 同时验证路径 + hasAutoSkillSource() 读取文件 frontmatter,两项均通过才 allow
  2. 与 Extract 合并调度scheduleSkillReview 检测是否有 pending/running extract 任务,如有则注入 shouldReviewSkillsAlso: true,由同一个 forked agent 用 combined prompt 同时完成 memory 和 skill 提炼,避免两次 LLM 调用
  3. 配置简化max_iterations=8timeoutMs=120_000 均硬编码,对外只保留 memory.enableAutoSkill 开关
  4. upsert 降级:review agent 创建新技能时若同名文件已存在且含 source: auto-skill,可通过 edit 追加更新而非全量覆盖

Reviewer Test Plan

npm run build && npm run bundle
mkdir /tmp/autoskill-test && cd /tmp/autoskill-test
git init && mkdir -p .qwen
echo '{ "memory": { "enableAutoSkill": true } }' > .qwen/settings.json
node /path/to/dist/cli.js "用 TypeScript 创建完整的 Express REST API" --approval-mode yolo 2>/dev/null
ls .qwen/skills/ && cat .qwen/skills/*/SKILL.md
场景 预期结果
enableAutoSkill: false 跑复杂任务 .qwen/skills/ 无变化
会话中写入 skills 目录后继续 sessionEnd 不再触发 review
review agent 尝试写 /tmp/ 路径错误,拒绝写入
review agent 尝试编辑无 source: auto-skill 的技能 permission sandbox 拒绝,返回 DENY
同时开启 autoMemory + autoSkill 只有一个 background agent,两类内容都写入

Testing Matrix

macOS Linux Windows
npm run
npx
Docker

Unit / Integration Test Results

 ✓ src/memory/manager.test.ts                        (21 tests)
 ✓ src/memory/skillReviewNudge.integration.test.ts   (18 tests)
 ✓ src/skills/skill-paths.test.ts                    (4 tests)

 Test Files  3 passed (3) | Tests  43 passed (43)

手动测试结果(macOS)

# 场景 结果
MT-1 功能关闭时不触发 .qwen/skills/ 无变化
MT-2 达到阈值后触发并生成 skill ✅ SKILL.md 自动创建,含顶层 source: auto-skill
MT-3 会话内写入 skills 目录后不重复触发 ✅ sessionEnd 未再触发额外 review
MT-4 review agent 写路径越界被拒绝 ✅ permission sandbox DENY
MT-5 review agent 不覆盖无 source: auto-skill 的技能 ✅ hasAutoSkillSource 返回 false,DENY
MT-6 下次会话可见新 skill ✅ 新会话中准确列出自动生成的 skill

Linked issues / bugs

N/A — 新功能

@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

Skill Nudge E2E 测试报告

1. 执行摘要

测试结果概览

指标 结果
测试文件 skillReviewNudge.integration.test.ts
测试用例数 18
通过数 18 ✅
失败数 0 ❌
跳过数 0 ⏭
通过率 100%
执行时间 2.16秒
平均单测耗时 1.2毫秒

2. 设计文档检查清单

根据设计文档 L258-320 的 8 项需求,测试覆盖情况如下:

# 设计文档要求 测试用例 状态 断言覆盖
1 toolCallCount < THRESHOLD 时不触发 Test 1.1 skipReason = 'low_density'
2 toolCallCount >= THRESHOLD 时触发调度 Test 2.1, 2.2 status = 'scheduled', taskId exists
3 自定义阈值支持 Test 2.3 useCustomThreshold 参数
4 历史中有 skill_manage 调用时跳过 Test 3 skipReason = 'recently_managed_skills'
5 skillNudge.enabled 配置门控 Test 4 enabled=false → skip, enabled=true → schedule
6 Extract 合并检测 Test 5 无 pending extract 时正常调度
7 任务元数据追踪 Test 6 metadata.isDensity, status transitions
8 阈值边界情形 (threshold-1, threshold, threshold+1) Test 7 3 个边界场景全覆盖

覆盖结论: 8/8 设计文档需求已验证 ✅


3. 详细测试结果

Test 1: 低工具调用密度不应触发技能评审

目的: 验证 toolCallCount < THRESHOLD 时不触发技能评审
状态: ✅ 2/2 通过

用例 断言 结果
1.1: toolCallCount (THRESHOLD-1) 时跳过 skipReason = 'low_density' ✅ PASS
1.2: toolCallCount = THRESHOLD-1 边界 status = 'skipped' ✅ PASS

Test 2: 到达或超过阈值应触发技能评审

目的: 验证 toolCallCount >= THRESHOLD 时正确调度
状态: ✅ 3/3 通过

用例 断言 结果
2.1: toolCallCount = THRESHOLD 时调度 status = 'scheduled' && taskId exists ✅ PASS
2.2: toolCallCount > THRESHOLD 时调度 taskId 格式正确 ✅ PASS
2.3: 自定义阈值被尊重 useCustomThreshold=true 时使用传入阈值 ✅ PASS

Test 3: 历史中 skill_manage 函数调用应阻止触发

目的: 验证最近已使用 skill_manage 时跳过
状态: ✅ 2/2 通过

用例 断言 结果
3.1: 历史包含 skill_manage 调用 skipReason = 'recently_managed_skills' ✅ PASS
3.2: 即使 toolCallCount >= THRESHOLD 也跳过 status = 'skipped' ✅ PASS

Test 4: 配置启用/禁用门控

目的: 验证 skillNudge.enabled 配置正确控制行为
状态: ✅ 3/3 通过

用例 断言 结果
4.1: enabled=false 时跳过 skipReason = 'disabled_in_config' ✅ PASS
4.2: config 未提供时跳过 status = 'skipped' ✅ PASS
4.3: enabled=true 时调度 status = 'scheduled' ✅ PASS

Test 5: Extract + Skill Review 合并检测

目的: 验证待处理的 extract 任务检测和独立调度
状态: ✅ 2/2 通过

用例 断言 结果
5.1: 无 pending extract 时正常调度 status = 'scheduled' && taskId exists ✅ PASS
5.2: 多个 skill review 任务独立调度 taskIds 唯一且都已追踪 ✅ PASS

Test 6: 任务记录追踪和元数据

目的: 验证任务元数据正确记录和状态转换
状态: ✅ 2/2 通过

用例 断言 结果
6.1: 任务记录包含正确元数据 metadata.isDensity=true, timestamp存在 ✅ PASS
6.2: 任务状态转换被追踪 'scheduled' → 'running' → 'completed' ✅ PASS

Test 7: 阈值边界情形

目的: 验证 threshold-1, threshold, threshold+1 的行为差异
状态: ✅ 3/3 通过

用例 toolCallCount 预期行为 结果
7.1: threshold - 1 19 (THRESHOLD=20) skipReason='low_density' ✅ PASS
7.2: threshold 20 status='scheduled' ✅ PASS
7.3: threshold + 1 21 status='scheduled' ✅ PASS

边界逻辑验证: 阈值在 >= 时触发(包含等于),是设计的意图。


@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 57.95% 57.95% 73.33% 79.65%
Core 76.88% 76.88% 79.87% 82.39%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   57.95 |    79.65 |   73.33 |   57.95 |                   
 src               |   67.85 |    62.09 |   74.19 |   67.85 |                   
  gemini.tsx       |   59.52 |    58.88 |   66.66 |   59.52 | ...62,770-773,781 
  ...ractiveCli.ts |   69.14 |    56.86 |   72.72 |   69.14 | ...88-795,817-818 
  ...liCommands.ts |   73.92 |     72.5 |     100 |   73.92 | ...40-264,289,389 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   46.11 |    63.01 |   55.88 |   46.11 |                   
  acpAgent.ts      |   47.91 |    63.38 |   62.06 |   47.91 | ...94-796,810-818 
  authMethods.ts   |   12.19 |      100 |       0 |   12.19 | 11-31,34-38,41-50 
  errorCodes.ts    |       0 |        0 |       0 |       0 | 1-22              
  ...DirContext.ts |     100 |      100 |     100 |     100 |                   
 ...ration/service |   68.65 |    83.33 |   66.66 |   68.65 |                   
  filesystem.ts    |   68.65 |    83.33 |   66.66 |   68.65 | ...32,77-94,97-98 
 ...ration/session |   74.98 |    68.71 |    82.6 |   74.98 |                   
  ...ryReplayer.ts |   65.93 |    75.67 |   81.81 |   65.93 | ...40-255,268-269 
  Session.ts       |   73.69 |    66.31 |   82.92 |   73.69 | ...2334,2340-2343 
  ...entTracker.ts |   90.85 |    84.84 |      90 |   90.85 | ...35,199,251-260 
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   84.21 |    77.77 |     100 |   84.21 | ...37-153,209-211 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ssion/emitters |   96.01 |    90.75 |    92.3 |   96.01 |                   
  BaseEmitter.ts   |   76.92 |    66.66 |      80 |   76.92 | 23-24,39-40,55-56 
  ...ageEmitter.ts |     100 |    89.47 |     100 |     100 | 109,111           
  PlanEmitter.ts   |     100 |      100 |     100 |     100 |                   
  ...allEmitter.ts |   98.06 |     92.3 |     100 |   98.06 | 227-228,327,335   
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
 ...ession/rewrite |   89.69 |    85.89 |   94.11 |   89.69 |                   
  LlmRewriter.ts   |   80.53 |    79.31 |     100 |   80.53 | ...17-119,170-174 
  ...Middleware.ts |   95.83 |    85.71 |     100 |   95.83 | 119,127-129       
  TurnBuffer.ts    |     100 |      100 |     100 |     100 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/auth          |   97.68 |    94.85 |   95.45 |   97.68 |                   
  allProviders.ts  |     100 |      100 |     100 |     100 |                   
  ...iderConfig.ts |    97.6 |    95.04 |     100 |    97.6 | ...61,411,433-434 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/auth/install  |     100 |    94.44 |     100 |     100 |                   
  ...nstallPlan.ts |     100 |    94.44 |     100 |     100 | 25,104            
 ...viders/alibaba |   97.57 |    85.71 |   66.66 |   97.57 |                   
  ...baStandard.ts |     100 |      100 |     100 |     100 |                   
  codingPlan.ts    |   94.93 |    85.71 |   66.66 |   94.93 | 87-89,94          
  tokenPlan.ts     |     100 |      100 |     100 |     100 |                   
 ...oviders/custom |     100 |      100 |     100 |     100 |                   
  ...omProvider.ts |     100 |      100 |     100 |     100 |                   
 ...roviders/oauth |    91.5 |    77.03 |   97.05 |    91.5 |                   
  openrouter.ts    |   84.37 |    33.33 |     100 |   84.37 | 43-48             
  ...outerOAuth.ts |    91.9 |    79.06 |   96.87 |    91.9 | ...53-655,699-701 
 ...ers/thirdParty |     100 |      100 |     100 |     100 |                   
  deepseek.ts      |     100 |      100 |     100 |     100 |                   
  idealab.ts       |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/commands      |   62.18 |      100 |    9.52 |   62.18 |                   
  auth.ts          |   46.91 |      100 |       0 |   46.91 | ...0,89-96,99-100 
  channel.ts       |   56.66 |      100 |       0 |   56.66 | 15-19,27-34       
  extensions.tsx   |   96.55 |      100 |      50 |   96.55 | 37                
  hooks.tsx        |   66.66 |      100 |       0 |   66.66 | 20-24             
  mcp.ts           |   94.73 |      100 |      50 |   94.73 | 28                
  review.ts        |   51.85 |      100 |       0 |   51.85 | 24-35,38          
 src/commands/auth |    68.1 |    84.04 |   63.63 |    68.1 |                   
  handler.ts       |   62.86 |    78.12 |   42.85 |   62.86 | ...94,590,649-653 
  ...veSelector.ts |     100 |    96.66 |     100 |     100 | 58                
 ...mmands/channel |    39.2 |    79.45 |      50 |    39.2 |                   
  ...l-registry.ts |    8.57 |      100 |       0 |    8.57 | 6-21,24-42        
  config-utils.ts  |   91.89 |      100 |   66.66 |   91.89 | 20-25             
  configure.ts     |    14.7 |      100 |       0 |    14.7 | 18-21,23-84       
  pairing.ts       |   26.31 |      100 |       0 |   26.31 | ...30,40-50,52-65 
  pidfile.ts       |   96.34 |    86.95 |     100 |   96.34 | 49,59,91          
  start.ts         |   31.15 |       52 |   69.23 |   31.15 | ...73-476,485-487 
  status.ts        |   17.54 |      100 |       0 |   17.54 | 15-26,32-77       
  stop.ts          |      20 |      100 |       0 |      20 | 14-48             
 ...nds/extensions |   84.53 |    88.95 |   81.81 |   84.53 |                   
  consent.ts       |   71.65 |    89.28 |   42.85 |   71.65 | ...85-141,156-162 
  disable.ts       |     100 |      100 |     100 |     100 |                   
  enable.ts        |     100 |      100 |     100 |     100 |                   
  install.ts       |    75.6 |    66.66 |   66.66 |    75.6 | ...39-142,145-153 
  link.ts          |     100 |      100 |     100 |     100 |                   
  list.ts          |     100 |      100 |     100 |     100 |                   
  new.ts           |     100 |      100 |     100 |     100 |                   
  settings.ts      |   99.15 |      100 |   83.33 |   99.15 | 151               
  uninstall.ts     |    37.5 |      100 |   33.33 |    37.5 | 23-45,57-64,67-70 
  update.ts        |   96.32 |      100 |     100 |   96.32 | 101-105           
  utils.ts         |   60.24 |    28.57 |     100 |   60.24 | ...81,83-87,89-93 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 src/commands/mcp  |   92.29 |    86.08 |   88.88 |   92.29 |                   
  add.ts           |     100 |    98.03 |     100 |     100 | 293               
  list.ts          |   91.22 |    80.76 |      80 |   91.22 | ...19-121,146-147 
  reconnect.ts     |   76.72 |    71.42 |   85.71 |   76.72 | 35-48,153-175     
  remove.ts        |     100 |       80 |     100 |     100 | 21-25             
 ...ommands/review |   11.57 |      100 |       0 |   11.57 |                   
  cleanup.ts       |   17.94 |      100 |       0 |   17.94 | ...01-106,108-109 
  deterministic.ts |   13.75 |      100 |       0 |   13.75 | ...22-738,740-741 
  fetch-pr.ts      |   11.36 |      100 |       0 |   11.36 | ...80-201,203-204 
  load-rules.ts    |   11.32 |      100 |       0 |   11.32 | ...41-153,155-156 
  pr-context.ts    |    6.22 |      100 |       0 |    6.22 | ...97-312,314-315 
  presubmit.ts     |    9.35 |      100 |       0 |    9.35 | ...62-287,289-290 
 ...nds/review/lib |      30 |      100 |       0 |      30 |                   
  gh.ts            |   22.58 |      100 |       0 |   22.58 | ...49,53-54,62-69 
  git.ts           |   22.72 |      100 |       0 |   22.72 | 15-18,29-39,43-44 
  paths.ts         |   52.94 |      100 |       0 |   52.94 | ...26,37-38,42-43 
 src/config        |   92.19 |    82.37 |   83.56 |   92.19 |                   
  auth.ts          |   86.98 |    80.32 |     100 |   86.98 | ...26-227,243-244 
  config.ts        |   86.41 |     82.4 |   72.72 |   86.41 | ...1343,1367-1368 
  keyBindings.ts   |   96.03 |       50 |     100 |   96.03 | 161-164           
  ...idersScope.ts |      92 |       90 |     100 |      92 | 11-12             
  sandboxConfig.ts |    58.9 |    61.53 |   66.66 |    58.9 | ...54-68,73,77-89 
  settings.ts      |    82.9 |    82.55 |   82.75 |    82.9 | ...39-940,945-948 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...tedFolders.ts |   96.29 |       94 |     100 |   96.29 | ...88-190,205-206 
 ...nfig/migration |   94.89 |    78.94 |   83.33 |   94.89 |                   
  index.ts         |   94.87 |    88.88 |     100 |   94.87 | 91-92             
  scheduler.ts     |   96.55 |    77.77 |     100 |   96.55 | 19-20             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ation/versions |   94.74 |       96 |     100 |   94.74 |                   
  ...-v2-shared.ts |     100 |      100 |     100 |     100 |                   
  v1-to-v2.ts      |   81.75 |    90.19 |     100 |   81.75 | ...28-229,231-247 
  v2-to-v3.ts      |     100 |      100 |     100 |     100 |                   
  v3-to-v4.ts      |     100 |      100 |     100 |     100 |                   
 src/core          |     100 |      100 |     100 |     100 |                   
  auth.ts          |     100 |      100 |     100 |     100 |                   
  initializer.ts   |     100 |      100 |     100 |     100 |                   
  theme.ts         |     100 |      100 |     100 |     100 |                   
 src/dualOutput    |   63.09 |    64.51 |   55.55 |   63.09 |                   
  ...tputBridge.ts |   62.94 |    65.51 |   56.25 |   62.94 | ...22-323,331-334 
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/export        |       0 |        0 |       0 |       0 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-7               
 src/generated     |     100 |      100 |     100 |     100 |                   
  git-commit.ts    |     100 |      100 |     100 |     100 |                   
 src/i18n          |   48.26 |    76.19 |   38.88 |   48.26 |                   
  index.ts         |   26.92 |    76.92 |   26.66 |   26.92 | ...38-239,249-260 
  languages.ts     |    98.7 |       75 |     100 |    98.7 | 110               
 src/i18n/locales  |       0 |        0 |       0 |       0 |                   
  ca.js            |       0 |        0 |       0 |       0 | 1-2146            
  de.js            |       0 |        0 |       0 |       0 | 1-2069            
  en.js            |       0 |        0 |       0 |       0 | 1-2138            
  fr.js            |       0 |        0 |       0 |       0 | 1-2102            
  ja.js            |       0 |        0 |       0 |       0 | 1-1560            
  pt.js            |       0 |        0 |       0 |       0 | 1-2060            
  ru.js            |       0 |        0 |       0 |       0 | 1-2065            
  zh-TW.js         |       0 |        0 |       0 |       0 | 1-1700            
  zh.js            |       0 |        0 |       0 |       0 | 1-1939            
 ...nonInteractive |   72.67 |    72.14 |   74.07 |   72.67 |                   
  session.ts       |   76.86 |    70.45 |   85.71 |   76.86 | ...78-779,787-797 
  types.ts         |    42.5 |      100 |   33.33 |    42.5 | ...80-581,584-585 
 ...active/control |   77.55 |    88.23 |      80 |   77.55 |                   
  ...rolContext.ts |    7.69 |        0 |       0 |    7.69 | 47-79             
  ...Dispatcher.ts |   91.66 |    91.83 |   88.88 |   91.66 | ...54-372,388,391 
  ...rolService.ts |       8 |        0 |       0 |       8 | 46-179            
 ...ol/controllers |    7.04 |       80 |   13.33 |    7.04 |                   
  ...Controller.ts |   19.32 |      100 |      60 |   19.32 | 81-118,127-210    
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |    3.96 |      100 |   11.11 |    3.96 | ...61-379,389-494 
  ...Controller.ts |   14.06 |      100 |       0 |   14.06 | ...82-117,130-133 
  ...Controller.ts |    5.21 |      100 |       0 |    5.21 | ...21-433,442-471 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |   97.59 |    93.06 |   95.18 |   97.59 |                   
  ...putAdapter.ts |   97.33 |    91.89 |   98.07 |   97.33 | ...1343,1368-1369 
  ...putAdapter.ts |      96 |    91.66 |   85.71 |      96 | 51-52             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.28 |      100 |      90 |   98.28 | 81-82,122-123     
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/remoteInput   |   86.98 |       75 |   85.71 |   86.98 |                   
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  ...putWatcher.ts |   88.12 |    76.08 |   91.66 |   88.12 | ...21-222,233-236 
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/services      |   90.74 |    89.75 |   97.14 |   90.74 |                   
  ...mandLoader.ts |     100 |     92.3 |     100 |     100 | 90                
  ...killLoader.ts |     100 |    96.15 |     100 |     100 | 44                
  ...andService.ts |    98.7 |      100 |     100 |    98.7 | 107               
  ...mandLoader.ts |   86.83 |    83.87 |     100 |   86.83 | ...30-335,340-345 
  ...omptLoader.ts |   75.32 |    80.64 |   83.33 |   75.32 | ...05-206,272-273 
  ...mandLoader.ts |     100 |      100 |     100 |     100 |                   
  ...nd-factory.ts |      91 |     90.9 |     100 |      91 | 123,132-139       
  ...ation-tool.ts |     100 |    95.45 |     100 |     100 | 125               
  commandUtils.ts  |      96 |       90 |     100 |      96 | 48                
  ...and-parser.ts |   90.69 |    85.71 |     100 |   90.69 | 63-66             
  ...ionService.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...ght/generators |   85.95 |    86.42 |   90.47 |   85.95 |                   
  DataProcessor.ts |   85.68 |    86.46 |   92.85 |   85.68 | ...1110,1114-1121 
  ...tGenerator.ts |   98.21 |    85.71 |     100 |   98.21 | 46                
  ...teRenderer.ts |   45.45 |      100 |       0 |   45.45 | 13-51             
 .../insight/types |       0 |       50 |      50 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 | 1                 
 ...mpt-processors |   97.27 |    94.04 |     100 |   97.27 |                   
  ...tProcessor.ts |     100 |      100 |     100 |     100 |                   
  ...eProcessor.ts |   94.52 |    84.21 |     100 |   94.52 | 46-47,93-94       
  ...tionParser.ts |     100 |      100 |     100 |     100 |                   
  ...lProcessor.ts |   97.41 |    95.65 |     100 |   97.41 | 95-98             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services/tips |   97.35 |    83.07 |     100 |   97.35 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  tipHistory.ts    |   92.45 |       70 |     100 |   92.45 | ...22,144,151,160 
  tipRegistry.ts   |     100 |    95.23 |     100 |     100 | 33                
  tipScheduler.ts  |     100 |    91.66 |     100 |     100 | 55                
 src/test-utils    |   93.75 |    83.33 |      80 |   93.75 |                   
  ...omMatchers.ts |   69.69 |       50 |      50 |   69.69 | 32-35,37-39,45-47 
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |     100 |      100 |     100 |     100 |                   
 src/ui            |   63.92 |    67.12 |   54.76 |   63.92 |                   
  App.tsx          |     100 |      100 |     100 |     100 |                   
  AppContainer.tsx |    66.6 |    61.15 |      75 |    66.6 | ...2330,2334-2338 
  ...tionNudge.tsx |    9.58 |      100 |       0 |    9.58 | 24-94             
  ...ackDialog.tsx |   29.23 |      100 |       0 |   29.23 | 25-75             
  ...tionNudge.tsx |    7.69 |      100 |       0 |    7.69 | 25-103            
  colors.ts        |   52.72 |      100 |   23.52 |   52.72 | ...52,54-55,60-61 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  keyMatchers.ts   |   95.91 |    96.42 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   48.01 |    58.73 |   21.42 |   48.01 |                   
  AuthDialog.tsx   |   64.26 |    44.44 |   16.66 |   64.26 | ...59,366-388,392 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |    9.61 |      100 |       0 |    9.61 | ...35-352,391-476 
  useAuth.ts       |   76.63 |    68.29 |     100 |   76.63 | ...48,493-499,560 
  ...rSetupFlow.ts |   44.61 |    33.33 |      50 |   44.61 | ...57-378,395-438 
 src/ui/commands   |   63.06 |       81 |   62.96 |   63.06 |                   
  aboutCommand.ts  |     100 |    85.71 |     100 |     100 | 36                
  agentsCommand.ts |   72.97 |      100 |      20 |   72.97 | ...32,37-38,42-44 
  ...odeCommand.ts |     100 |      100 |     100 |     100 |                   
  arenaCommand.ts  |   33.13 |    67.64 |    37.5 |   33.13 | ...60-565,644-649 
  authCommand.ts   |     100 |      100 |     100 |     100 |                   
  branchCommand.ts |     100 |      100 |     100 |     100 |                   
  btwCommand.ts    |   95.59 |    71.42 |     100 |   95.59 | 72,154-159        
  bugCommand.ts    |   77.35 |    66.66 |      50 |   77.35 | 21-22,60-69       
  clearCommand.ts  |   90.58 |    73.68 |      50 |   90.58 | ...46,74-75,93-94 
  ...essCommand.ts |   63.39 |       48 |      50 |   63.39 | ...48-149,163-166 
  ...extCommand.ts |    6.17 |      100 |      10 |    6.17 | ...21-522,527-528 
  copyCommand.ts   |   98.28 |    94.89 |     100 |   98.28 | ...80,280,321,327 
  deleteCommand.ts |     100 |      100 |     100 |     100 |                   
  ...ryCommand.tsx |   66.11 |    76.74 |   55.55 |   66.11 | ...05-306,315-323 
  docsCommand.ts   |   96.07 |     87.5 |      50 |   96.07 | 20-21             
  doctorCommand.ts |     100 |    93.33 |     100 |     100 | 21                
  dreamCommand.ts  |      75 |    66.66 |   66.66 |      75 | 22-27,44-47       
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |   56.93 |    91.66 |   33.33 |   56.93 | ...52-353,361-362 
  ...onsCommand.ts |   45.08 |    85.71 |   27.27 |   45.08 | ...37-238,247-248 
  forgetCommand.ts |   26.82 |      100 |      50 |   26.82 | 18-51             
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |   19.04 |       25 |      20 |   19.04 | ...86-187,204-205 
  ideCommand.ts    |   57.33 |    57.69 |   35.29 |   57.33 | ...05-306,310-324 
  initCommand.ts   |   84.33 |    72.72 |     100 |   84.33 | 68,82-87,89-94    
  ...ghtCommand.ts |    72.8 |    66.66 |   83.33 |    72.8 | ...31-245,250-273 
  ...ageCommand.ts |   89.39 |    82.35 |   76.92 |   89.39 | ...22-325,348-349 
  ...elsCommand.ts |     100 |      100 |     100 |     100 |                   
  mcpCommand.ts    |   86.66 |      100 |      50 |   86.66 | 14-15             
  memoryCommand.ts |   86.66 |      100 |      50 |   86.66 | 14-15             
  modelCommand.ts  |   74.44 |    79.06 |   71.42 |   74.44 | ...90-199,222-227 
  ...onsCommand.ts |     100 |      100 |     100 |     100 |                   
  planCommand.ts   |   78.82 |    76.92 |     100 |   78.82 | 30-35,51-56,68-73 
  quitCommand.ts   |   93.93 |      100 |      50 |   93.93 | 15-16             
  recapCommand.ts  |   21.81 |      100 |      50 |   21.81 | 24-73             
  ...berCommand.ts |   32.43 |      100 |      50 |   32.43 | 23-57             
  renameCommand.ts |   85.61 |    78.18 |     100 |   85.61 | ...15-322,329-334 
  ...oreCommand.ts |    92.3 |     87.5 |     100 |    92.3 | ...,83-88,129-130 
  resumeCommand.ts |     100 |      100 |     100 |     100 |                   
  rewindCommand.ts |      80 |      100 |      50 |      80 | 19-21             
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |   81.43 |    65.21 |      80 |   81.43 | ...70-173,176-179 
  skillsCommand.ts |   15.04 |      100 |      25 |   15.04 | ...90-106,109-136 
  statsCommand.ts  |   83.91 |    81.25 |      50 |   83.91 | ...31-132,142-145 
  ...ineCommand.ts |     100 |      100 |     100 |     100 |                   
  ...aryCommand.ts |    6.51 |      100 |      50 |    6.51 | 28-323            
  tasksCommand.ts  |   77.45 |    73.43 |     100 |   77.45 | ...55-159,181-186 
  ...tupCommand.ts |     100 |      100 |     100 |     100 |                   
  themeCommand.ts  |     100 |      100 |     100 |     100 |                   
  toolsCommand.ts  |   95.23 |      100 |      50 |   95.23 | 18-19             
  trustCommand.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  vimCommand.ts    |   54.54 |      100 |      50 |   54.54 | 19-29             
 src/ui/components |   59.76 |    73.57 |    63.8 |   59.76 |                   
  AboutBox.tsx     |     100 |      100 |     100 |     100 |                   
  AnsiOutput.tsx   |   65.57 |      100 |      50 |   65.57 | 69-90             
  ApiKeyInput.tsx  |       0 |        0 |       0 |       0 | 1-97              
  AppHeader.tsx    |   89.39 |       75 |     100 |   89.39 | 35,37-42,44       
  ...odeDialog.tsx |     9.7 |      100 |       0 |     9.7 | 35-47,50-182      
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   14.63 |      100 |       0 |   14.63 | 18-56             
  ...TextInput.tsx |   77.01 |       76 |     100 |   77.01 | ...20,234-236,263 
  Composer.tsx     |   79.31 |    57.14 |     100 |   79.31 | ...-77,95,133,146 
  ...entPrompt.tsx |     100 |      100 |     100 |     100 |                   
  ...ryDisplay.tsx |   75.89 |    62.06 |     100 |   75.89 | ...,88,93-108,113 
  ...geDisplay.tsx |   68.42 |    57.14 |     100 |   68.42 | 16-17,31-32,42-50 
  ...ification.tsx |   28.57 |      100 |       0 |   28.57 | 16-36             
  ...gProfiler.tsx |       0 |        0 |       0 |       0 | 1-36              
  ...ogManager.tsx |   12.66 |      100 |       0 |   12.66 | 62-457            
  ...ngsDialog.tsx |    8.44 |      100 |       0 |    8.44 | 37-195            
  ExitWarning.tsx  |     100 |      100 |     100 |     100 |                   
  ...hProgress.tsx |    87.8 |    33.33 |     100 |    87.8 | 28-31,56          
  ...ustDialog.tsx |     100 |      100 |     100 |     100 |                   
  Footer.tsx       |   79.67 |    58.06 |     100 |   79.67 | ...98-102,104-108 
  ...ngSpinner.tsx |   68.42 |       80 |      50 |   68.42 | 35-52,73,80-81    
  Header.tsx       |   98.38 |    92.59 |     100 |   98.38 | 139,141           
  Help.tsx         |   98.74 |    68.75 |     100 |   98.74 | 74,129            
  ...emDisplay.tsx |   63.22 |     37.5 |     100 |   63.22 | ...25-334,337,340 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   81.63 |    76.68 |   83.33 |   81.63 | ...1339,1404,1454 
  ...Shortcuts.tsx |   20.87 |      100 |       0 |   20.87 | ...6,49-51,67-125 
  ...Indicator.tsx |     100 |    91.42 |     100 |     100 | 65,74             
  ...firmation.tsx |   91.42 |      100 |      50 |   91.42 | 26-31             
  MainContent.tsx  |   77.31 |    59.37 |     100 |   77.31 | ...48-252,260-264 
  ...elsDialog.tsx |   16.07 |    89.18 |      50 |   16.07 | ...58-159,162-648 
  MemoryDialog.tsx |   53.35 |    51.21 |   57.14 |   53.35 | ...55,367,380-382 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   76.31 |    54.94 |     100 |   76.31 | ...05-521,578-582 
  ...tsDisplay.tsx |     100 |    96.96 |     100 |     100 | 234               
  ...fications.tsx |   18.18 |      100 |       0 |   18.18 | 15-58             
  ...onsDialog.tsx |    2.13 |      100 |       0 |    2.13 | 62-133,148-1004   
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...icePrompt.tsx |   88.14 |    83.87 |     100 |   88.14 | ...01-105,133-138 
  PrepareLabel.tsx |   91.66 |    76.19 |     100 |   91.66 | 73-75,77-79,110   
  ...atePrompt.tsx |    8.57 |      100 |       0 |    8.57 | 24-55,58-134      
  ...geDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ngDisplay.tsx |   21.42 |      100 |       0 |   21.42 | 13-39             
  ...hProgress.tsx |   85.25 |    88.46 |     100 |   85.25 | 121-147           
  ...dSelector.tsx |    4.45 |      100 |       0 |    4.45 | 28-92,100-328     
  ...ionPicker.tsx |   85.77 |       80 |     100 |   85.77 | ...89-301,335-337 
  ...onPreview.tsx |   91.73 |    78.26 |     100 |   91.73 | ...,70-71,126-128 
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...putPrompt.tsx |   72.56 |       80 |      40 |   72.56 | ...06-109,114-117 
  ...ngsDialog.tsx |   66.88 |    73.52 |     100 |   66.88 | ...11-819,825-826 
  ...ionDialog.tsx |    87.8 |      100 |   33.33 |    87.8 | 36-39,44-51       
  ...putPrompt.tsx |    15.9 |      100 |       0 |    15.9 | 20-63             
  ...Indicator.tsx |   57.14 |      100 |       0 |   57.14 | 12-15             
  ...MoreLines.tsx |      28 |      100 |       0 |      28 | 18-40             
  ...ionPicker.tsx |   17.59 |      100 |       0 |   17.59 | 55-172            
  StatsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...yTodoList.tsx |   94.17 |       80 |     100 |   94.17 | 56-57,131-134     
  ...nsDisplay.tsx |   84.09 |    57.14 |     100 |   84.09 | ...16-118,125-127 
  ThemeDialog.tsx  |   89.95 |    46.15 |      75 |   89.95 | ...71-173,243-245 
  Tips.tsx         |   93.75 |       75 |     100 |   93.75 | 39-40             
  TodoDisplay.tsx  |     100 |      100 |     100 |     100 |                   
  ...tsDisplay.tsx |     100 |     87.5 |     100 |     100 | 31-32             
  TrustDialog.tsx  |     100 |    81.81 |     100 |     100 | 71-86             
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
  ...ackDialog.tsx |    7.84 |      100 |       0 |    7.84 | 24-134            
 ...nts/agent-view |    25.2 |       90 |      10 |    25.2 |                   
  ...atContent.tsx |    8.79 |      100 |       0 |    8.79 | 53-265,271-273    
  ...tChatView.tsx |   21.05 |      100 |       0 |   21.05 | 21-39             
  ...tComposer.tsx |    9.95 |      100 |       0 |    9.95 | 57-308            
  AgentFooter.tsx  |   17.07 |      100 |       0 |   17.07 | 28-66             
  AgentHeader.tsx  |   15.38 |      100 |       0 |   15.38 | 27-64             
  AgentTabBar.tsx  |    8.13 |      100 |       0 |    8.13 | 39-59,64-187      
  ...oryAdapter.ts |     100 |    91.83 |     100 |     100 | 103,109-110,138   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |   45.72 |    70.53 |   60.86 |   45.72 |                   
  ArenaCards.tsx   |   73.06 |    71.79 |   85.71 |   73.06 | ...83-185,321-326 
  ...ectDialog.tsx |   83.48 |    69.86 |   88.88 |   83.48 | ...88-392,409-410 
  ...artDialog.tsx |   10.15 |      100 |       0 |   10.15 | 27-161            
  ...tusDialog.tsx |    5.63 |      100 |       0 |    5.63 | 33-75,80-288      
  ...topDialog.tsx |    6.17 |      100 |       0 |    6.17 | 33-213            
 ...ackground-view |   76.62 |    83.66 |   85.71 |   76.62 |                   
  ...sksDialog.tsx |   71.23 |    78.83 |   73.33 |   71.23 | ...1066,1142-1144 
  ...TasksPill.tsx |   70.83 |    86.95 |     100 |   70.83 | 44,84-96,104-112  
  ...gentPanel.tsx |   99.52 |    93.18 |     100 |   99.52 | 123               
 ...nts/extensions |   45.28 |    33.33 |      60 |   45.28 |                   
  ...gerDialog.tsx |   44.31 |    34.14 |      75 |   44.31 | ...71-480,483-488 
  index.ts         |       0 |        0 |       0 |       0 | 1-9               
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...tensions/steps |   54.77 |    94.23 |   66.66 |   54.77 |                   
  ...ctionStep.tsx |   95.12 |    92.85 |   85.71 |   95.12 | 84-86,89          
  ...etailStep.tsx |    6.18 |      100 |       0 |    6.18 | 17-128            
  ...nListStep.tsx |   88.35 |    94.73 |      80 |   88.35 | 51-52,58-71,105   
  ...electStep.tsx |   13.46 |      100 |       0 |   13.46 | 20-70             
  ...nfirmStep.tsx |   19.56 |      100 |       0 |   19.56 | 23-65             
  index.ts         |     100 |      100 |     100 |     100 |                   
 ...mponents/hooks |   72.24 |    70.52 |      80 |   72.24 |                   
  ...etailStep.tsx |   96.52 |       75 |     100 |   96.52 | 33,37,50,59       
  ...etailStep.tsx |   93.27 |    73.68 |     100 |   93.27 | 41-42,99-104,110  
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   36.09 |    47.05 |      50 |   36.09 | ...49,453-466,470 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |    20.2 |    84.61 |   81.81 |    20.2 |                   
  ...ealthPill.tsx |   68.42 |    85.71 |     100 |   68.42 | 40-46             
  ...entDialog.tsx |    3.64 |      100 |       0 |    3.64 | 41-717            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-30              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   96.42 |    87.09 |     100 |   96.42 | 21,96-97          
 ...ents/mcp/steps |    6.65 |      100 |       0 |    6.65 |                   
  ...icateStep.tsx |     5.1 |      100 |       0 |     5.1 | 34-95,98-334      
  ...electStep.tsx |   10.95 |      100 |       0 |   10.95 | 16-88             
  ...etailStep.tsx |    5.26 |      100 |       0 |    5.26 | 31-247            
  ...rListStep.tsx |    5.88 |      100 |       0 |    5.88 | 20-176            
  ...etailStep.tsx |   10.41 |      100 |       0 |   10.41 | ...1,67-79,82-139 
  ToolListStep.tsx |    7.14 |      100 |       0 |    7.14 | 16-146            
 ...nents/messages |   81.64 |    80.34 |   71.64 |   81.64 |                   
  ...ionDialog.tsx |   77.35 |    74.54 |    62.5 |   77.35 | ...90,508,526-528 
  BtwMessage.tsx   |     100 |      100 |     100 |     100 |                   
  ...upDisplay.tsx |   97.67 |    83.72 |     100 |   97.67 | 119,142,150       
  ...onMessage.tsx |   91.93 |    82.35 |     100 |   91.93 | 57-59,61,63       
  ...nMessages.tsx |   79.06 |      100 |      70 |   79.06 | ...51-264,268-280 
  DiffRenderer.tsx |   93.19 |    86.17 |     100 |   93.19 | ...09,237-238,304 
  ...ssMessage.tsx |    12.5 |      100 |       0 |    12.5 | 18-59             
  ...edMessage.tsx |   16.66 |      100 |       0 |   16.66 | 22-38             
  ...sMessages.tsx |   55.67 |       40 |   28.57 |   55.67 | ...20-125,133-145 
  ...ryMessage.tsx |   12.82 |      100 |       0 |   12.82 | 22-59             
  ...onMessage.tsx |   81.02 |    69.23 |   33.33 |   81.02 | ...24-426,433-435 
  ...upMessage.tsx |      84 |    93.61 |     100 |      84 | ...56-383,405-420 
  ToolMessage.tsx  |   88.84 |    75.71 |    92.3 |   88.84 | ...44-749,776-778 
 ...ponents/shared |   82.37 |    77.36 |   92.75 |   82.37 |                   
  ...ctionList.tsx |   99.03 |    95.65 |     100 |   99.03 | 85                
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  EnumSelector.tsx |     100 |    96.42 |     100 |     100 | 58                
  MaxSizedBox.tsx  |   83.01 |    86.25 |   88.88 |   83.01 | ...12-513,618-619 
  MultiSelect.tsx  |    6.29 |      100 |       0 |    6.29 | 35-42,45-176      
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  TextInput.tsx    |   72.98 |    55.55 |      80 |   72.98 | ...08-212,224-230 
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  text-buffer.ts   |   83.62 |    75.62 |   97.61 |   83.62 | ...2272,2300,2368 
  ...er-actions.ts |   86.71 |    67.79 |     100 |   86.71 | ...07-608,809-811 
 ...ents/subagents |   30.87 |        0 |       0 |   30.87 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
  reducers.tsx     |    12.1 |      100 |       0 |    12.1 | 33-190            
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   10.95 |      100 |       0 |   10.95 | ...1,56-57,60-102 
 ...bagents/create |    9.13 |      100 |       0 |    9.13 |                   
  ...ionWizard.tsx |    7.28 |      100 |       0 |    7.28 | 34-299            
  ...rSelector.tsx |   14.75 |      100 |       0 |   14.75 | 26-85             
  ...onSummary.tsx |    4.26 |      100 |       0 |    4.26 | 27-331            
  ...tionInput.tsx |    8.63 |      100 |       0 |    8.63 | 23-177            
  ...dSelector.tsx |   33.33 |      100 |       0 |   33.33 | 20-21,26-27,36-63 
  ...nSelector.tsx |    37.5 |      100 |       0 |    37.5 | 20-21,26-27,36-58 
  ...EntryStep.tsx |   12.76 |      100 |       0 |   12.76 | 34-78             
  ToolSelector.tsx |    4.16 |      100 |       0 |    4.16 | 31-253            
 ...bagents/manage |    8.39 |      100 |       0 |    8.39 |                   
  ...ctionStep.tsx |   10.25 |      100 |       0 |   10.25 | 21-103            
  ...eleteStep.tsx |   20.93 |      100 |       0 |   20.93 | 23-62             
  ...tEditStep.tsx |   25.53 |      100 |       0 |   25.53 | ...2,37-38,51-124 
  ...ctionStep.tsx |    2.29 |      100 |       0 |    2.29 | 28-449            
  ...iewerStep.tsx |   13.72 |      100 |       0 |   13.72 | 18-73             
  ...gerDialog.tsx |    6.74 |      100 |       0 |    6.74 | 35-341            
 ...mponents/views |   42.16 |    69.23 |   21.42 |   42.16 |                   
  ContextUsage.tsx |     4.7 |      100 |       0 |     4.7 | ...52-167,170-456 
  DoctorReport.tsx |     9.8 |      100 |       0 |     9.8 | 25-54,57-131      
  ...sionsList.tsx |   87.69 |    73.68 |     100 |   87.69 | 65-72             
  McpStatus.tsx    |   89.53 |    60.52 |     100 |   89.53 | ...72,175-177,262 
  SkillsList.tsx   |   27.27 |      100 |       0 |   27.27 | 18-35             
  ToolsList.tsx    |     100 |      100 |     100 |     100 |                   
 src/ui/contexts   |   77.04 |    78.24 |   82.14 |   77.04 |                   
  ...ewContext.tsx |   65.77 |      100 |      75 |   65.77 | ...22-225,231-241 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |   93.37 |    68.57 |      50 |   93.37 | ...94-195,222-226 
  ...deContext.tsx |     100 |      100 |     100 |     100 |                   
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   81.88 |    82.26 |     100 |   81.88 | ...1153,1159-1161 
  ...owContext.tsx |   89.28 |       80 |   66.66 |   89.28 | 34,47-48,60-62    
  ...deContext.tsx |     100 |      100 |      50 |     100 |                   
  ...onContext.tsx |   43.28 |     62.5 |    62.5 |   43.28 | ...56-259,263-266 
  ...gsContext.tsx |   83.33 |       50 |     100 |   83.33 | 17-18             
  ...usContext.tsx |     100 |      100 |     100 |     100 |                   
  ...ngContext.tsx |   71.42 |       50 |     100 |   71.42 | 17-20             
  ...utContext.tsx |   85.71 |      100 |   66.66 |   85.71 | 13-14             
  ...nsContext.tsx |   88.23 |       50 |     100 |   88.23 | 102-103           
  ...teContext.tsx |   85.71 |       50 |     100 |   85.71 | 168-169           
  ...deContext.tsx |   76.08 |    72.72 |     100 |   76.08 | 47-48,52-59,77-78 
 src/ui/editors    |   93.33 |    85.71 |   66.66 |   93.33 |                   
  ...ngsManager.ts |   93.33 |    85.71 |   66.66 |   93.33 | 49,63-64          
 src/ui/hooks      |   80.79 |     80.9 |   86.34 |   80.79 |                   
  ...dProcessor.ts |   83.12 |    82.56 |     100 |   83.12 | ...88-389,408-435 
  keyToAnsi.ts     |    3.92 |      100 |       0 |    3.92 | 19-77             
  ...dProcessor.ts |    94.8 |    70.58 |     100 |    94.8 | ...76-277,282-283 
  ...dProcessor.ts |   74.42 |    61.19 |   61.53 |   74.42 | ...16,840,859-863 
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-158            
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...ationFrame.ts |      32 |       60 |     100 |      32 | 42-44,51-90       
  ...odeCommand.ts |   58.82 |      100 |     100 |   58.82 | 28,33-48          
  ...enaCommand.ts |      85 |      100 |     100 |      85 | 23-24,29          
  ...aInProcess.ts |   19.81 |    66.66 |      25 |   19.81 | 57-175            
  ...Completion.ts |   92.77 |    89.09 |     100 |   92.77 | ...86-187,220-223 
  ...ifications.ts |   92.07 |    96.29 |     100 |   92.07 | 116-124           
  ...tIndicator.ts |     100 |    93.75 |     100 |     100 | 63                
  ...waySummary.ts |   96.22 |    69.69 |     100 |   96.22 | 125-127,169       
  ...ndTaskView.ts |   94.11 |    76.92 |     100 |   94.11 | 119-123,216,222   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   93.75 |    73.17 |     100 |   93.75 | ...68-169,221-222 
  ...ompletion.tsx |   91.28 |    79.59 |     100 |   91.28 | ...20-221,259-269 
  ...dMigration.ts |   90.62 |       75 |     100 |   90.62 | 38-40             
  useCompletion.ts |    92.4 |     87.5 |     100 |    92.4 | 68-69,93-94,98-99 
  ...nitMessage.ts |     100 |      100 |     100 |     100 |                   
  ...extualTips.ts |   76.92 |       50 |     100 |   76.92 | 55,68,71-75,88-96 
  ...eteCommand.ts |   33.33 |       50 |     100 |   33.33 | 30,34,41-90       
  ...ialogClose.ts |   18.18 |      100 |     100 |   18.18 | 75-130            
  ...oublePress.ts |   53.12 |       75 |     100 |   53.12 | 33-35,41-54       
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...Completion.ts |   99.12 |     97.7 |     100 |   99.12 | 182-183           
  ...ionUpdates.ts |   93.45 |     92.3 |     100 |   93.45 | ...83-287,300-306 
  ...agerDialog.ts |   88.88 |      100 |     100 |   88.88 | 21,25             
  ...backDialog.ts |   54.88 |       50 |   33.33 |   54.88 | ...71-173,195-196 
  useFocus.ts      |     100 |      100 |     100 |     100 |                   
  ...olderTrust.ts |     100 |      100 |     100 |     100 |                   
  ...ggestions.tsx |   67.46 |       90 |      50 |   67.46 | ...09-130,149-150 
  ...miniStream.ts |   75.92 |    72.95 |   91.66 |   75.92 | ...2300,2313-2321 
  ...BranchName.ts |    90.9 |     92.3 |     100 |    90.9 | 19-20,55-58       
  ...oryManager.ts |   93.15 |    93.75 |     100 |   93.15 | 44,107-110        
  ...ooksDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...stListener.ts |     100 |      100 |     100 |     100 |                   
  ...nAuthError.ts |   76.19 |       50 |     100 |   76.19 | 39-40,43-45       
  ...putHistory.ts |   92.59 |    85.71 |     100 |   92.59 | 63-64,72,94-96    
  ...storyStore.ts |     100 |    94.11 |     100 |     100 | 69                
  useKeypress.ts   |     100 |      100 |     100 |     100 |                   
  ...rdProtocol.ts |   36.36 |      100 |       0 |   36.36 | 24-31             
  ...unchEditor.ts |    9.67 |      100 |       0 |    9.67 | 11-32,39-90       
  ...gIndicator.ts |     100 |      100 |     100 |     100 |                   
  useLogger.ts     |   21.05 |      100 |       0 |   21.05 | 15-37             
  useMCPHealth.ts  |   63.15 |       75 |      50 |   63.15 | 42-52,64-67       
  ...elsCommand.ts |     100 |      100 |     100 |     100 |                   
  useMcpDialog.ts  |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...moryDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...oryMonitor.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...delCommand.ts |     100 |       75 |     100 |     100 | 22                
  ...raseCycler.ts |   84.74 |    76.47 |     100 |   84.74 | ...49,52-53,69-71 
  ...derUpdates.ts |   86.38 |    77.19 |     100 |   86.38 | ...22,281-293,341 
  useQwenAuth.ts   |     100 |      100 |     100 |     100 |                   
  ...lScheduler.ts |   84.52 |    93.33 |     100 |   84.52 | ...27-232,328-338 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...umeCommand.ts |   97.24 |    76.92 |     100 |   97.24 | 104-105,145       
  ...ompletion.tsx |   90.59 |    83.33 |     100 |   90.59 | ...01,104,137-140 
  ...ectionList.ts |   96.96 |    95.74 |     100 |   96.96 | ...82-183,237-240 
  ...sionPicker.ts |   79.79 |    61.19 |     100 |   79.79 | ...02-404,413-415 
  ...earchInput.ts |     100 |      100 |     100 |     100 |                   
  ...ngsCommand.ts |   18.75 |      100 |       0 |   18.75 | 10-25             
  ...ellHistory.ts |   91.74 |    79.41 |     100 |   91.74 | ...74,122-123,133 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-73              
  ...Completion.ts |   78.99 |    81.48 |   94.11 |   78.99 | ...77-579,587-624 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  useStatusLine.ts |     100 |    98.79 |     100 |     100 | 257               
  ...eateDialog.ts |   88.23 |      100 |     100 |   88.23 | 14,18             
  ...tification.ts |     100 |    85.71 |     100 |     100 | 47                
  ...alProgress.ts |   53.06 |       50 |   66.66 |   53.06 | ...53,61-68,79-85 
  ...rminalSize.ts |   76.19 |      100 |      50 |   76.19 | 21-25             
  ...emeCommand.ts |   67.01 |    29.41 |     100 |   67.01 | ...10-111,115-116 
  useTimer.ts      |   88.09 |    85.71 |     100 |   88.09 | 44-45,51-53       
  ...lMigration.ts |       0 |        0 |       0 |       0 |                   
  ...rustModify.ts |     100 |      100 |     100 |     100 |                   
  ...elcomeBack.ts |   87.36 |     90.9 |     100 |   87.36 | ...,94-96,114-115 
  vim.ts           |   83.77 |    80.31 |     100 |   83.77 | ...55,759-767,776 
 src/ui/layouts    |   89.72 |     87.5 |     100 |   89.72 |                   
  ...AppLayout.tsx |   89.88 |     87.5 |     100 |   89.88 | 51-53,93-98       
  ...AppLayout.tsx |   89.47 |     87.5 |     100 |   89.47 | 58-63             
 ...i/manageModels |   93.61 |       48 |     100 |   93.61 |                   
  manageModels.ts  |   93.61 |       48 |     100 |   93.61 | ...63-166,179,209 
 src/ui/models     |   80.24 |    79.16 |   71.42 |   80.24 |                   
  ...ableModels.ts |   80.24 |    79.16 |   71.42 |   80.24 | ...,61-71,123-125 
 ...noninteractive |     100 |      100 |    7.14 |     100 |                   
  ...eractiveUi.ts |     100 |      100 |    7.14 |     100 |                   
 src/ui/state      |   94.91 |    81.81 |     100 |   94.91 |                   
  extensions.ts    |   94.91 |    81.81 |     100 |   94.91 | 68-69,88          
 src/ui/themes     |   98.53 |    70.31 |     100 |   98.53 |                   
  ansi-light.ts    |     100 |      100 |     100 |     100 |                   
  ansi.ts          |     100 |      100 |     100 |     100 |                   
  atom-one-dark.ts |     100 |      100 |     100 |     100 |                   
  ayu-light.ts     |     100 |      100 |     100 |     100 |                   
  ayu.ts           |     100 |      100 |     100 |     100 |                   
  color-utils.ts   |     100 |      100 |     100 |     100 |                   
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  ...inal-theme.ts |   88.59 |    85.45 |     100 |   88.59 | ...57-261,266-270 
  dracula.ts       |     100 |      100 |     100 |     100 |                   
  github-dark.ts   |     100 |      100 |     100 |     100 |                   
  github-light.ts  |     100 |      100 |     100 |     100 |                   
  googlecode.ts    |     100 |      100 |     100 |     100 |                   
  no-color.ts      |     100 |      100 |     100 |     100 |                   
  qwen-dark.ts     |     100 |      100 |     100 |     100 |                   
  qwen-light.ts    |     100 |      100 |     100 |     100 |                   
  ...tic-tokens.ts |     100 |      100 |     100 |     100 |                   
  ...-of-purple.ts |     100 |      100 |     100 |     100 |                   
  theme-manager.ts |   87.98 |    82.89 |     100 |   87.98 | ...48-357,362-363 
  theme.ts         |     100 |    38.02 |     100 |     100 | ...34-449,457-461 
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |    82.6 |    81.62 |   91.38 |    82.6 |                   
  ...Colorizer.tsx |   82.78 |    88.23 |     100 |   82.78 | ...10-111,197-223 
  ...nRenderer.tsx |   57.89 |    55.31 |      50 |   57.89 | ...86-188,208-227 
  ...wnDisplay.tsx |   86.01 |    87.41 |     100 |   86.01 | ...87,704,729-754 
  ...idDiagram.tsx |   87.79 |    95.34 |     100 |   87.79 | 156-179           
  ...eRenderer.tsx |   94.74 |    82.22 |   94.11 |   94.74 | ...91,503,506-509 
  ...dWorkUtils.ts |     100 |      100 |     100 |     100 |                   
  ...boardUtils.ts |   59.61 |    58.82 |     100 |   59.61 | ...,86-88,107-149 
  commandUtils.ts  |    84.7 |    88.13 |      90 |    84.7 | ...63-164,260-279 
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  customBanner.ts  |   90.68 |    91.22 |     100 |   90.68 | ...13,324-327,334 
  displayUtils.ts  |   88.37 |    72.22 |     100 |   88.37 | 23,25,29,31,33    
  formatters.ts    |   95.23 |    98.27 |     100 |   95.23 | 117-120           
  gradientUtils.ts |     100 |      100 |     100 |     100 |                   
  highlight.ts     |   98.63 |       95 |     100 |   98.63 | 93                
  ...oryMapping.ts |     100 |    94.28 |     100 |     100 | 34,56             
  isNarrowWidth.ts |     100 |      100 |     100 |     100 |                   
  ...olDetector.ts |    8.23 |      100 |       0 |    8.23 | ...31-132,135-136 
  latexRenderer.ts |   94.95 |     73.8 |     100 |   94.95 | ...76-178,184-187 
  layoutUtils.ts   |     100 |      100 |     100 |     100 |                   
  ...nUtilities.ts |   69.84 |    85.71 |     100 |   69.84 | 75-91,100-101     
  ...ToolGroups.ts |   98.52 |    96.42 |     100 |   98.52 | 48-49             
  ...geRenderer.ts |   86.23 |    69.06 |   95.12 |   86.23 | ...1284,1324-1330 
  ...alRenderer.ts |   86.69 |     71.9 |     100 |   86.69 | ...1476,1513-1519 
  ...lsBySource.ts |     100 |    95.23 |     100 |     100 | 84                
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  ...storyUtils.ts |   61.06 |    69.62 |      90 |   61.06 | ...64,412,417-439 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  ...izedOutput.ts |   94.94 |      100 |   88.88 |   94.94 | 112-117           
  ...wOptimizer.ts |     100 |    96.77 |     100 |     100 | 69                
  terminalSetup.ts |    4.37 |      100 |       0 |    4.37 | 44-393            
  textUtils.ts     |   97.35 |    94.38 |   91.66 |   97.35 | ...50-251,386-387 
  todoSnapshot.ts  |   89.11 |    93.18 |     100 |   89.11 | ...,66-78,180-181 
  updateCheck.ts   |     100 |    80.95 |     100 |     100 | 30-42             
 ...i/utils/export |   56.77 |     40.8 |   79.41 |   56.77 |                   
  collect.ts       |   55.92 |    50.58 |   86.36 |   55.92 | ...25-640,642-647 
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |   57.47 |    20.51 |      80 |   57.47 | ...09-310,324-359 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
  utils.ts         |      40 |      100 |       0 |      40 | 11-13             
 ...ort/formatters |    3.38 |      100 |       0 |    3.38 |                   
  html.ts          |    9.61 |      100 |       0 |    9.61 | ...28,34-76,82-84 
  json.ts          |      50 |      100 |       0 |      50 | 14-15             
  jsonl.ts         |     3.5 |      100 |       0 |     3.5 | 14-76             
  markdown.ts      |    0.94 |      100 |       0 |    0.94 | 13-295            
 src/utils         |   73.81 |    89.71 |   93.68 |   73.81 |                   
  acpModelUtils.ts |     100 |      100 |     100 |     100 |                   
  apiPreconnect.ts |   96.52 |    97.05 |     100 |   96.52 | 164-167           
  checks.ts        |   33.33 |      100 |       0 |   33.33 | 23-28             
  cleanup.ts       |   84.12 |    93.33 |      80 |   84.12 | 75,106-115        
  commands.ts      |     100 |      100 |     100 |     100 |                   
  commentJson.ts   |   85.29 |    89.47 |     100 |   85.29 | 48-57             
  ...Calculator.ts |     100 |      100 |     100 |     100 |                   
  deepMerge.ts     |     100 |       90 |     100 |     100 | 41-43,49          
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  doctorChecks.ts  |   68.59 |    64.28 |     100 |   68.59 | ...63-269,293-309 
  ...putCapture.ts |   90.65 |    86.17 |     100 |   90.65 | ...72,370,372-373 
  ...arResolver.ts |   94.28 |    88.46 |     100 |   94.28 | 28-29,125-126     
  errors.ts        |   98.63 |    96.15 |     100 |   98.63 | 67-68             
  events.ts        |     100 |      100 |     100 |     100 |                   
  gitUtils.ts      |   91.91 |    84.61 |     100 |   91.91 | 78-81,124-127     
  ...AutoUpdate.ts |   90.76 |    93.33 |   88.88 |   90.76 | 103-114           
  ...lationInfo.ts |     100 |      100 |     100 |     100 |                   
  languageUtils.ts |   97.89 |    96.42 |     100 |   97.89 | 132-133           
  math.ts          |       0 |        0 |       0 |       0 | 1-15              
  ...onfigUtils.ts |     100 |      100 |     100 |     100 |                   
  ...iveHelpers.ts |   96.79 |    93.28 |     100 |   96.79 | ...76-477,575,588 
  osc.ts           |    97.5 |      100 |   88.88 |    97.5 | 195-196           
  package.ts       |   88.88 |       80 |     100 |   88.88 | 33-34             
  processUtils.ts  |     100 |      100 |     100 |     100 |                   
  readStdin.ts     |   79.62 |       90 |      80 |   79.62 | 33-40,52-54       
  relaunch.ts      |   98.07 |    76.92 |     100 |   98.07 | 70                
  resolvePath.ts   |   66.66 |       25 |     100 |   66.66 | 12-13,16,18-19    
  sandbox.ts       |       0 |        0 |       0 |       0 | 1-980             
  settingsUtils.ts |   82.89 |    90.67 |   89.47 |   82.89 | ...52-663,670-678 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...upProfiler.ts |     100 |    95.83 |     100 |     100 | 110               
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       60 |     100 |     100 | 23,32             
  systemInfo.ts    |   92.52 |     90.9 |   83.33 |   92.52 | 63-69,184         
  ...InfoFields.ts |    87.5 |     64.1 |     100 |    87.5 | ...21-122,143-144 
  ...iffPreview.ts |   94.11 |    83.33 |     100 |   94.11 | 13                
  ...entEmitter.ts |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |   91.17 |    82.35 |     100 |   91.17 | 67-68,73-74,77-78 
  version.ts       |     100 |       50 |     100 |     100 | 11                
  windowTitle.ts   |     100 |      100 |     100 |     100 |                   
  ...WithBackup.ts |    62.1 |    77.77 |     100 |    62.1 | 93,107,118-157    
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   76.88 |    82.39 |   79.87 |   76.88 |                   
 src               |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/__mocks__/fs  |       0 |        0 |       0 |       0 |                   
  promises.ts      |       0 |        0 |       0 |       0 | 1-48              
 src/agents        |   84.06 |    76.02 |   90.66 |   84.06 |                   
  ...transcript.ts |   88.76 |    75.43 |     100 |   88.76 | ...82,306-307,434 
  ...ent-resume.ts |   78.64 |    69.51 |   76.66 |   78.64 | ...87-991,994-996 
  ...ound-tasks.ts |   94.19 |    86.17 |     100 |   94.19 | ...36-637,654-655 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/arena  |    76.9 |    66.66 |   78.94 |    76.9 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |   75.84 |     62.9 |   78.57 |   75.84 | ...1889,1895-1896 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  diff-summary.ts  |    87.5 |    73.46 |     100 |    87.5 | ...32-133,137-138 
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...gents/backends |   76.29 |    86.15 |   73.04 |   76.29 |                   
  ITermBackend.ts  |   97.97 |    93.93 |     100 |   97.97 | ...78-180,255,307 
  ...essBackend.ts |   91.25 |    90.62 |   86.66 |   91.25 | ...94,249-269,328 
  TmuxBackend.ts   |    90.7 |    76.55 |   97.36 |    90.7 | ...87,697,743-747 
  detect.ts        |   31.25 |      100 |       0 |   31.25 | 34-88             
  index.ts         |     100 |      100 |     100 |     100 |                   
  iterm-it2.ts     |     100 |     92.1 |     100 |     100 | 37-38,106         
  tmux-commands.ts |    6.64 |      100 |    3.03 |    6.64 | ...93-363,386-503 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...agents/runtime |    80.2 |    76.22 |   68.22 |    80.2 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   74.32 |    70.18 |   52.77 |   74.32 | ...1384,1411-1457 
  agent-events.ts  |     100 |      100 |     100 |     100 |                   
  ...t-headless.ts |   79.27 |    69.76 |   52.38 |   79.27 | ...81-382,385-386 
  ...nteractive.ts |   79.71 |    79.62 |      75 |   79.71 | ...54,456,458,461 
  ...statistics.ts |   98.19 |    82.35 |     100 |   98.19 | 127,151,192,225   
  agent-types.ts   |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/config        |   75.48 |    77.95 |   62.28 |   75.48 |                   
  config.ts        |   73.28 |    75.76 |      57 |   73.28 | ...2981,2985-2997 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   95.72 |    92.98 |   91.66 |   95.72 | ...06-207,241-242 
 ...nfirmation-bus |   98.29 |    97.14 |     100 |   98.29 |                   
  message-bus.ts   |   98.14 |    97.05 |     100 |   98.14 | 42-43             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/core          |    82.9 |    82.27 |   88.64 |    82.9 |                   
  baseLlmClient.ts |   96.77 |    96.42 |      80 |   96.77 | 123-126           
  client.ts        |   77.33 |    78.15 |   87.09 |   77.33 | ...1417,1421-1437 
  ...tGenerator.ts |    72.1 |    61.11 |     100 |    72.1 | ...63,365,372-375 
  ...lScheduler.ts |   77.64 |    81.23 |   92.85 |   77.64 | ...2226,2278-2282 
  geminiChat.ts    |   88.88 |    83.62 |   86.11 |   88.88 | ...1228,1295-1296 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  ...htProtocol.ts |    9.09 |      100 |       0 |    9.09 | 34-42,45-49,52-87 
  logger.ts        |   82.25 |    81.81 |     100 |   82.25 | ...57-361,407-421 
  ...tyDefaults.ts |     100 |      100 |     100 |     100 |                   
  ...olExecutor.ts |   92.59 |       75 |      50 |   92.59 | 41-42             
  ...on-helpers.ts |   85.71 |    70.58 |     100 |   85.71 | ...90-191,205-214 
  ...issionFlow.ts |   98.59 |    94.73 |     100 |   98.59 | 93                
  prompts.ts       |    88.8 |    88.05 |      75 |    88.8 | ...-898,1101-1102 
  tokenLimits.ts   |     100 |    89.47 |     100 |     100 | 51-52             
  ...okTriggers.ts |   99.31 |    91.02 |     100 |   99.31 | 124,135           
  turn.ts          |   96.42 |    88.88 |     100 |   96.42 | ...00,413-414,462 
 ...ntentGenerator |    94.6 |    79.35 |   92.68 |    94.6 |                   
  ...tGenerator.ts |   96.49 |    79.35 |      90 |   96.49 | ...24,481,637,693 
  converter.ts     |   94.38 |    79.78 |     100 |   94.38 | ...40-541,551,734 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
 ...ntentGenerator |   91.53 |    71.64 |   93.33 |   91.53 |                   
  ...tGenerator.ts |      90 |    70.96 |   92.85 |      90 | ...80-286,304-305 
  index.ts         |     100 |       80 |     100 |     100 | 50                
 ...ntentGenerator |   91.02 |    83.54 |   88.46 |   91.02 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   90.99 |    83.54 |   88.46 |   90.99 | ...51,661-662,690 
 ...ntentGenerator |    79.7 |       84 |   89.47 |    79.7 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   74.92 |    80.85 |   86.95 |   74.92 | ...1410,1431-1437 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |   52.38 |    44.44 |      50 |   52.38 | ...77,81-85,89-93 
  ...tGenerator.ts |   48.78 |    91.66 |   77.77 |   48.78 | ...10-163,166-167 
  pipeline.ts      |   93.65 |     84.9 |     100 |   93.65 | ...79-480,488,553 
  ...ureContext.ts |     100 |      100 |     100 |     100 |                   
  ...ingOptions.ts |       0 |        0 |       0 |       0 | 1                 
  ...CallParser.ts |   90.66 |     88.4 |     100 |   90.66 | ...15-319,349-350 
  ...kingParser.ts |     100 |    96.87 |     100 |     100 | 42                
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...rator/provider |   96.62 |     88.3 |   95.45 |   96.62 |                   
  dashscope.ts     |   97.22 |    87.69 |   93.33 |   97.22 | ...10-211,287-288 
  deepseek.ts      |   95.55 |    90.56 |     100 |   95.55 | ...31-132,145-146 
  default.ts       |   94.62 |    86.36 |   85.71 |   94.62 | 85-86,156-158     
  index.ts         |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  mistral.ts       |   96.07 |    73.33 |     100 |   96.07 | 32-33             
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 |                   
 src/extension     |   60.62 |    79.43 |   79.03 |   60.62 |                   
  ...-converter.ts |   62.35 |    47.82 |      90 |   62.35 | ...90-791,800-832 
  ...ionManager.ts |   46.92 |    82.19 |   67.44 |   46.92 | ...1386,1396-1415 
  ...onSettings.ts |   93.46 |    93.05 |     100 |   93.46 | ...17-221,228-232 
  ...-converter.ts |   54.88 |    94.44 |      60 |   54.88 | ...35-146,158-192 
  github.ts        |   44.94 |    88.52 |      60 |   44.94 | ...53-359,398-451 
  index.ts         |     100 |      100 |     100 |     100 |                   
  marketplace.ts   |   97.29 |    93.75 |     100 |   97.29 | ...64,184-185,274 
  npm.ts           |   48.66 |    76.08 |      75 |   48.66 | ...18-420,427-431 
  override.ts      |   94.11 |    88.88 |     100 |   94.11 | 63-64,81-82       
  settings.ts      |   66.26 |      100 |      50 |   66.26 | 81-108,143-149    
  storage.ts       |   94.73 |       90 |     100 |   94.73 | 41-42             
  ...ableSchema.ts |     100 |      100 |     100 |     100 |                   
  variables.ts     |   88.75 |    83.33 |     100 |   88.75 | ...28-231,234-237 
 src/followup      |   46.35 |     92.3 |   71.87 |   46.35 |                   
  followupState.ts |      96 |    89.74 |     100 |      96 | 159-161,218-219   
  index.ts         |     100 |      100 |     100 |     100 |                   
  overlayFs.ts     |   95.06 |       84 |     100 |   95.06 | 78,108,122,133    
  speculation.ts   |   13.22 |      100 |   16.66 |   13.22 | 88-458,518-568    
  ...onToolGate.ts |     100 |    96.29 |     100 |     100 | 93                
  ...nGenerator.ts |   36.67 |    95.12 |   33.33 |   36.67 | ...24-326,361-391 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/hooks         |   80.61 |    84.37 |   84.16 |   80.61 |                   
  ...okRegistry.ts |   86.48 |    77.08 |     100 |   86.48 | ...41-344,362-369 
  ...bortSignal.ts |     100 |      100 |     100 |     100 |                   
  ...terpolator.ts |   96.66 |    93.33 |     100 |   96.66 | 66-67             
  ...HookRunner.ts |   96.68 |    87.23 |     100 |   96.68 | 110-112,231-233   
  ...Aggregator.ts |   96.37 |    90.54 |     100 |   96.37 | ...89,291-292,365 
  ...entHandler.ts |   95.58 |    84.37 |   92.59 |   95.58 | ...29,682-683,693 
  hookPlanner.ts   |   84.13 |    76.59 |      90 |   84.13 | ...38,144,162-173 
  hookRegistry.ts  |   88.83 |    86.36 |     100 |   88.83 | ...21,326,330,334 
  hookRunner.ts    |   53.63 |    72.22 |   61.11 |   53.63 | ...23-724,733-734 
  hookSystem.ts    |   75.47 |      100 |   56.41 |   75.47 | ...75-576,582-583 
  ...HookRunner.ts |   75.51 |     61.9 |      80 |   75.51 | ...05-406,424-425 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...SkillHooks.ts |   78.75 |       75 |   66.66 |   78.75 | 62-66,137-152     
  ...oksManager.ts |    96.5 |     91.8 |     100 |    96.5 | ...90,209-210,223 
  ssrfGuard.ts     |   77.22 |    85.36 |     100 |   77.22 | ...57,261-267,273 
  trustedHooks.ts  |       0 |        0 |       0 |       0 | 1-124             
  types.ts         |   90.18 |    91.02 |   85.18 |   90.18 | ...91-392,452-456 
  urlValidator.ts  |     100 |      100 |     100 |     100 |                   
 src/ide           |   74.28 |    83.39 |   78.33 |   74.28 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |    64.2 |    81.48 |   66.66 |    64.2 | ...9-970,999-1007 
  ide-installer.ts |   89.06 |    79.31 |     100 |   89.06 | ...36,143-147,160 
  ideContext.ts    |     100 |      100 |     100 |     100 |                   
  process-utils.ts |   84.84 |    71.79 |     100 |   84.84 | ...37,151,193-194 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/lsp           |   33.92 |    45.16 |   45.76 |   33.92 |                   
  ...nfigLoader.ts |   70.27 |    35.89 |   94.73 |   70.27 | ...20-422,426-432 
  ...ionFactory.ts |    4.29 |      100 |       0 |    4.29 | ...20-371,377-394 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   13.52 |    81.25 |   29.16 |   13.52 | ...75-694,700-730 
  ...eLspClient.ts |   17.89 |      100 |       0 |   17.89 | ...37-244,254-258 
  ...LspService.ts |   45.87 |    62.13 |   66.66 |   45.87 | ...1282,1299-1309 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mcp           |   78.69 |    75.34 |   75.92 |   78.69 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...h-provider.ts |   86.95 |      100 |   33.33 |   86.95 | ...,93,97,101-102 
  ...h-provider.ts |   73.82 |    53.92 |     100 |   73.82 | ...88-895,902-904 
  ...en-storage.ts |   98.62 |    97.72 |     100 |   98.62 | 87-88             
  oauth-utils.ts   |   70.58 |    85.29 |    90.9 |   70.58 | ...70-290,315-344 
  ...n-provider.ts |   89.83 |    95.83 |   45.45 |   89.83 | ...43,147,151-152 
 .../token-storage |   79.48 |    86.66 |   86.36 |   79.48 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   82.75 |    82.35 |   92.85 |   82.75 | ...62-172,180-181 
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   68.14 |    82.35 |   64.28 |   68.14 | ...81-295,298-314 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/memory        |   59.03 |     75.8 |   63.12 |   59.03 |                   
  const.ts         |     100 |      100 |     100 |     100 |                   
  dream.ts         |   65.65 |    73.33 |      50 |   65.65 | 50,107-148        
  ...entPlanner.ts |   57.84 |    72.72 |   33.33 |   57.84 | ...35,140-147,152 
  entries.ts       |   59.84 |       70 |      50 |   59.84 | ...72-180,183-189 
  extract.ts       |    95.2 |    79.16 |     100 |    95.2 | 81-86,125         
  ...entPlanner.ts |   63.08 |    65.71 |   41.17 |   63.08 | ...17,222-223,332 
  ...ionPlanner.ts |       0 |        0 |       0 |       0 | 1                 
  forget.ts        |    8.04 |      100 |       0 |    8.04 | 67-342            
  governance.ts    |       0 |        0 |       0 |       0 | 1-352             
  indexer.ts       |   83.87 |    45.45 |     100 |   83.87 | ...50,56-57,69-70 
  manager.ts       |   75.31 |    81.04 |    75.6 |   75.31 | ...1278,1291-1293 
  memoryAge.ts     |   90.47 |    77.77 |     100 |   90.47 | 50-51             
  paths.ts         |   55.47 |    89.47 |   85.71 |   55.47 | ...,88-89,105-113 
  prompt.ts        |   93.36 |    71.42 |     100 |   93.36 | ...58,161,228-229 
  recall.ts        |   79.56 |    69.38 |   88.88 |   79.56 | ...40-245,269-280 
  ...ceSelector.ts |   91.95 |    77.27 |     100 |   91.95 | ...08,110-111,119 
  scan.ts          |   87.91 |    68.42 |     100 |   87.91 | ...47-48,58,82-87 
  ...entPlanner.ts |    11.5 |      100 |       0 |    11.5 | ...57-192,210-298 
  status.ts        |   10.52 |      100 |       0 |   10.52 | 41-98             
  store.ts         |   94.44 |    83.33 |     100 |   94.44 | 56-57,92-93       
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mocks         |       0 |        0 |       0 |       0 |                   
  msw.ts           |       0 |        0 |       0 |       0 | 1-9               
 src/models        |   89.31 |    85.95 |    87.5 |   89.31 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...tor-config.ts |   90.24 |    91.42 |     100 |   90.24 | 142,148,151-160   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nfigErrors.ts |   74.22 |    47.82 |   84.61 |   74.22 | ...,67-74,106-117 
  ...igResolver.ts |   98.63 |    92.53 |     100 |   98.63 | 161,323,329       
  modelRegistry.ts |     100 |    98.59 |     100 |     100 | 222               
  modelsConfig.ts  |   84.57 |    81.92 |   81.57 |   84.57 | ...1223,1252-1253 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/output        |     100 |      100 |     100 |     100 |                   
  ...-formatter.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/permissions   |   71.18 |    88.73 |   48.57 |   71.18 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...on-manager.ts |   81.42 |    86.66 |      80 |   81.42 | ...19-820,827-836 
  rule-parser.ts   |   95.99 |    93.18 |     100 |   95.99 | ...-864,1013-1015 
  ...-semantics.ts |   58.28 |    85.27 |    30.2 |   58.28 | ...1604-1614,1643 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/prompts       |   83.63 |      100 |    87.5 |   83.63 |                   
  mcp-prompts.ts   |   18.18 |      100 |       0 |   18.18 | 11-19             
  ...t-registry.ts |     100 |      100 |     100 |     100 |                   
 src/qwen          |   86.03 |    79.48 |   97.18 |   86.03 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   85.01 |    74.81 |   93.33 |   85.01 | ...,986-1002,1032 
  ...kenManager.ts |   83.79 |    76.22 |     100 |   83.79 | ...63-768,789-794 
 src/services      |   86.47 |    84.48 |   89.76 |   86.47 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   97.82 |    94.73 |     100 |   97.82 | 172-173           
  ...ionService.ts |    95.5 |    94.11 |     100 |    95.5 | ...92,349,351-355 
  ...ingService.ts |   82.99 |     83.2 |   81.81 |   82.99 | ...1136,1153-1154 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  cronScheduler.ts |   97.56 |    92.98 |     100 |   97.56 | 62-63,77,155      
  ...eryService.ts |   80.43 |    95.45 |      75 |   80.43 | ...19-134,140-141 
  fileReadCache.ts |     100 |      100 |     100 |     100 |                   
  ...temService.ts |   89.76 |     85.1 |   88.88 |   89.76 | ...89,191,266-273 
  ...ratedFiles.ts |      96 |    88.23 |     100 |      96 | 119-120,146-147   
  gitInit.ts       |     100 |      100 |     100 |     100 |                   
  gitService.ts    |   68.75 |     92.3 |   55.55 |   68.75 | ...12-122,125-129 
  ...reeService.ts |   71.83 |    68.47 |    91.3 |   71.83 | ...89-790,806,822 
  ...ionService.ts |   98.13 |     97.8 |   95.45 |   98.13 | ...32-333,380-381 
  ...orRegistry.ts |   96.76 |    92.15 |     100 |   96.76 | ...95-396,449-450 
  sessionRecap.ts  |   10.71 |      100 |       0 |   10.71 | 48-161            
  ...ionService.ts |   89.07 |    76.68 |   96.29 |   89.07 | ...1164,1168-1169 
  sessionTitle.ts  |   93.95 |    70.37 |     100 |   93.95 | ...36-239,270-271 
  ...ionService.ts |   83.01 |    78.66 |   87.75 |   83.01 | ...1482,1488-1493 
  ...UseSummary.ts |    94.7 |    88.67 |     100 |    94.7 | ...69-171,221-222 
 ...icrocompaction |   98.62 |    86.44 |     100 |   98.62 |                   
  microcompact.ts  |   98.62 |    86.44 |     100 |   98.62 | 138,142           
 src/skills        |   87.48 |     83.7 |   94.23 |   87.48 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...activation.ts |     100 |     93.1 |     100 |     100 | 93,112            
  skill-load.ts    |   92.94 |    81.63 |     100 |   92.94 | ...06,226,238-240 
  skill-manager.ts |   83.28 |    79.42 |   90.32 |   83.28 | ...1111,1118-1122 
  skill-paths.ts   |   86.74 |    77.77 |     100 |   86.74 | ...00-101,106-107 
  symlinkScope.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/subagents     |   82.87 |    79.74 |   95.23 |   82.87 |                   
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...-selection.ts |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |    76.8 |    71.42 |   92.85 |    76.8 | ...1157,1179-1180 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 51-56,69-74,78-83 
 src/telemetry     |   70.23 |    85.14 |   75.11 |   70.23 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...-exporters.ts |   46.37 |      100 |   44.44 |   46.37 | ...85,88-89,92-93 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-111             
  ...-processor.ts |   91.66 |    81.81 |   92.85 |   91.66 | ...83-188,203-204 
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-128             
  loggers.ts       |    51.9 |       64 |   57.77 |    51.9 | ...1214,1231-1251 
  metrics.ts       |    74.9 |    82.95 |   74.54 |    74.9 | ...58-978,981-992 
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  sdk.ts           |   89.53 |    81.53 |     100 |   89.53 | ...84-285,304-308 
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |   79.09 |    94.39 |   83.33 |   79.09 | ...1134,1137-1166 
  uiTelemetry.ts   |   92.97 |    96.96 |   81.25 |   92.97 | ...93-194,200-207 
 ...ry/qwen-logger |   68.01 |    80.21 |   64.91 |   68.01 |                   
  event-types.ts   |       0 |        0 |       0 |       0 |                   
  qwen-logger.ts   |   68.01 |       80 |   64.28 |   68.01 | ...1042,1080-1081 
 src/test-utils    |   93.07 |    95.55 |   73.52 |   93.07 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  ...st-helpers.ts |   94.11 |       90 |     100 |   94.11 | 69-70             
  index.ts         |     100 |      100 |     100 |     100 |                   
  mock-tool.ts     |   91.02 |    96.77 |   68.96 |   91.02 | ...32,196-197,210 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |   75.34 |    80.62 |   83.71 |   75.34 |                   
  ...erQuestion.ts |    88.8 |    76.74 |    90.9 |    88.8 | ...36-337,344-345 
  cron-create.ts   |   97.61 |    88.88 |   83.33 |   97.61 | 30-31             
  cron-delete.ts   |   96.55 |      100 |   83.33 |   96.55 | 26-27             
  cron-list.ts     |   96.36 |      100 |   83.33 |   96.36 | 25-26             
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  edit.ts          |   78.01 |    84.76 |   73.33 |   78.01 | ...86-687,774-824 
  exitPlanMode.ts  |   84.61 |    85.71 |     100 |   84.61 | ...60-163,177-189 
  glob.ts          |   90.63 |    88.33 |   84.61 |   90.63 | ...28,171,302,305 
  grep.ts          |   79.19 |    85.71 |   78.94 |   79.19 | ...20,560,569-576 
  ls.ts            |   96.74 |    90.27 |     100 |   96.74 | 176-181,212,216   
  lsp.ts           |   72.69 |    60.09 |   90.32 |   72.69 | ...1208,1210-1211 
  ...nt-manager.ts |   51.95 |     65.9 |   47.36 |   51.95 | ...03-525,528-565 
  mcp-client.ts    |   32.44 |    75.28 |   63.63 |   32.44 | ...1462,1466-1469 
  mcp-tool.ts      |   90.92 |    88.88 |   96.42 |   90.92 | ...89-590,640-641 
  memory-config.ts |       0 |        0 |       0 |       0 | 1-48              
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 102,109           
  monitor.ts       |   92.16 |    83.45 |      92 |   92.16 | ...15,544-547,560 
  ...nforcement.ts |   84.86 |    88.23 |     100 |   84.86 | 161-172,221-234   
  read-file.ts     |   95.09 |     88.6 |      90 |   95.09 | ...99,271-274,277 
  ripGrep.ts       |   94.59 |    85.71 |   93.33 |   94.59 | ...60,463,541-542 
  ...-transport.ts |    6.34 |        0 |       0 |    6.34 | 47-145            
  send-message.ts  |   88.77 |    91.66 |   83.33 |   88.77 | 44-45,68-76       
  shell.ts         |   70.33 |    79.54 |   88.88 |   70.33 | ...3587,3636-3642 
  skill-utils.ts   |     100 |      100 |     100 |     100 |                   
  skill.ts         |   88.11 |    91.17 |   84.61 |   88.11 | ...95,399,422-444 
  task-stop.ts     |   92.94 |    96.15 |   85.71 |   92.94 | 39-40,54-64       
  todoWrite.ts     |   85.42 |    84.09 |   84.61 |   85.42 | ...05-410,432-433 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   70.73 |    68.67 |   74.28 |   70.73 | ...76-677,685-686 
  tools.ts         |    87.6 |    89.79 |   88.23 |    87.6 | ...31-432,448-454 
  web-fetch.ts     |   88.44 |    76.92 |    92.3 |   88.44 | ...05-306,308-309 
  write-file.ts    |   79.29 |    79.26 |   83.33 |   79.29 | ...36-639,651-686 
 src/tools/agent   |   83.07 |    83.83 |    82.6 |   83.07 |                   
  agent.ts         |   83.37 |    84.29 |   82.92 |   83.37 | ...1542,1551-1555 
  fork-subagent.ts |   78.26 |    71.42 |      80 |   78.26 | 54-72,104-105     
 src/utils         |   87.73 |    87.39 |   92.21 |   87.73 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   76.08 |    44.44 |     100 |   76.08 | 61-70,72          
  bareMode.ts      |   27.27 |      100 |       0 |   27.27 | 9-15,18-19        
  browser.ts       |    7.69 |      100 |       0 |    7.69 | 17-56             
  ...igResolver.ts |     100 |      100 |     100 |     100 |                   
  ...engthError.ts |   89.11 |    86.66 |     100 |   89.11 | ...28-129,132-133 
  cronDisplay.ts   |   42.85 |    23.07 |     100 |   42.85 | 26-31,33-45,47-54 
  cronParser.ts    |   89.74 |    85.71 |     100 |   89.74 | ...,63-64,183-186 
  debugLogger.ts   |   96.12 |    93.75 |   93.75 |   96.12 | 164-168           
  editHelper.ts    |   93.63 |    83.52 |     100 |   93.63 | ...28-429,463-464 
  editor.ts        |   97.61 |    95.71 |     100 |   97.61 | ...70-271,273-274 
  ...arResolver.ts |   94.28 |    88.88 |     100 |   94.28 | 28-29,125-126     
  ...entContext.ts |     100 |    95.45 |     100 |     100 | 83                
  errorParsing.ts  |    97.7 |    97.05 |     100 |    97.7 | 72-73             
  ...rReporting.ts |   88.46 |       90 |     100 |   88.46 | 69-74             
  errors.ts        |   70.92 |    79.59 |   53.33 |   70.92 | ...03-219,223-229 
  fetch.ts         |   70.18 |    71.42 |   71.42 |   70.18 | ...42,148,161,186 
  fileUtils.ts     |   89.18 |    85.13 |   94.73 |   89.18 | ...91-898,902-908 
  forkedAgent.ts   |    78.5 |    70.73 |   85.71 |    78.5 | ...30-436,441-447 
  formatters.ts    |   54.54 |       50 |     100 |   54.54 | 12-16             
  ...eUtilities.ts |   89.21 |    86.66 |     100 |   89.21 | 16-17,49-55,65-66 
  ...rStructure.ts |   94.36 |    94.28 |     100 |   94.36 | ...17-120,330-335 
  getPty.ts        |    12.5 |      100 |       0 |    12.5 | 21-34             
  ...noreParser.ts |    92.3 |    89.36 |     100 |    92.3 | ...15-116,186-187 
  gitUtils.ts      |   38.88 |    84.61 |      50 |   38.88 | ...2,51-74,97-148 
  iconvHelper.ts   |     100 |      100 |     100 |     100 |                   
  ...rePatterns.ts |     100 |      100 |     100 |     100 |                   
  ...ionManager.ts |     100 |     90.9 |     100 |     100 | 26                
  ...lPromptIds.ts |     100 |      100 |     100 |     100 |                   
  jsonl-utils.ts   |   61.42 |    92.45 |   45.45 |   61.42 | ...68-301,307-313 
  ...-detection.ts |     100 |      100 |     100 |     100 |                   
  ...yDiscovery.ts |   83.85 |    79.36 |     100 |   83.85 | ...15,318,410-413 
  ...tProcessor.ts |   93.63 |       90 |     100 |   93.63 | ...96-302,384-385 
  ...Inspectors.ts |   61.53 |      100 |      50 |   61.53 | 18-23             
  ...kerChecker.ts |   82.55 |    78.57 |     100 |   82.55 | 68-69,79-84,92-98 
  notebook.ts      |   94.35 |    84.78 |     100 |   94.35 | ...10,122,174-176 
  openaiLogger.ts  |   86.27 |    82.14 |     100 |   86.27 | ...05-107,130-135 
  partUtils.ts     |     100 |      100 |     100 |     100 |                   
  pathReader.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |   93.21 |    91.86 |     100 |   93.21 | ...89-390,392-394 
  pdf.ts           |   93.68 |    87.05 |     100 |   93.68 | ...96-297,321-325 
  projectPath.ts   |     100 |      100 |     100 |     100 |                   
  ...ectSummary.ts |   89.39 |    72.41 |     100 |   89.39 | ...37-142,193-196 
  ...tIdContext.ts |     100 |      100 |     100 |     100 |                   
  proxyUtils.ts    |     100 |      100 |     100 |     100 |                   
  ...rDetection.ts |   58.57 |       76 |     100 |   58.57 | ...4,88-89,95-100 
  ...noreParser.ts |   85.45 |    85.18 |     100 |   85.45 | ...59,65-66,72-73 
  rateLimit.ts     |   92.55 |    85.92 |     100 |   92.55 | ...70-272,309-310 
  readManyFiles.ts |   87.96 |    86.95 |     100 |   87.96 | ...05-207,223-234 
  retry.ts         |   89.81 |    88.05 |     100 |   89.81 | ...29,350,357-358 
  ripgrepUtils.ts  |   46.53 |    84.37 |   66.66 |   46.53 | ...32-233,245-322 
  ...sDiscovery.ts |   97.42 |    92.85 |     100 |   97.42 | ...04,182-183,202 
  ...tchOptions.ts |   63.85 |    64.28 |   83.33 |   63.85 | ...29-130,187-188 
  safeJsonParse.ts |   74.07 |    83.33 |     100 |   74.07 | 40-46             
  ...nStringify.ts |     100 |      100 |     100 |     100 |                   
  ...aConverter.ts |   90.78 |    87.87 |     100 |   90.78 | ...41-42,93,95-96 
  ...aValidator.ts |   93.43 |    77.04 |     100 |   93.43 | ...46,155-158,212 
  ...r-launcher.ts |   76.92 |     91.3 |   66.66 |   76.92 | ...34,136,157-195 
  ...orageUtils.ts |   92.41 |    82.65 |     100 |   92.41 | ...39,423-430,441 
  shell-utils.ts   |   82.93 |    89.55 |     100 |   82.93 | ...1522,1529-1533 
  ...lAstParser.ts |   95.58 |    85.79 |     100 |   95.58 | ...1059-1061,1071 
  ...nlyChecker.ts |   95.75 |    92.39 |     100 |   95.75 | ...00-301,313-314 
  sideQuery.ts     |     100 |    92.85 |     100 |     100 | 43                
  ...tGenerator.ts |     100 |      100 |     100 |     100 |                   
  ...ameContext.ts |     100 |      100 |     100 |     100 |                   
  symlink.ts       |   77.77 |       50 |     100 |   77.77 | 44,54-59          
  ...emEncoding.ts |   96.36 |    91.17 |     100 |   96.36 | 59-60,124-125     
  terminalSafe.ts  |     100 |      100 |     100 |     100 |                   
  ...Serializer.ts |   98.72 |       90 |     100 |   98.72 | 42-43,134,201-203 
  testUtils.ts     |   53.33 |      100 |   33.33 |   53.33 | ...53,59-64,70-72 
  textUtils.ts     |      60 |      100 |   66.66 |      60 | 36-55             
  thoughtUtils.ts  |     100 |    92.85 |     100 |     100 | 71                
  ...-converter.ts |   94.59 |    85.71 |     100 |   94.59 | 35-36             
  tool-utils.ts    |    93.6 |     91.3 |     100 |    93.6 | ...58-159,162-163 
  truncation.ts    |     100 |       92 |     100 |     100 | 52,71             
  windowsPath.ts   |   89.47 |    79.31 |     100 |   89.47 | ...57-58,62,90-91 
  ...aceContext.ts |   93.71 |    88.88 |   93.33 |   93.71 | ...24-225,249-251 
  xml.ts           |     100 |      100 |     100 |     100 |                   
  yaml-parser.ts   |      92 |    84.31 |     100 |      92 | 49-53,65-69       
 ...ils/filesearch |   96.34 |    91.66 |     100 |   96.34 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |   96.87 |    94.44 |     100 |   96.87 | 83-84             
  fileSearch.ts    |   93.29 |    86.76 |     100 |   93.29 | ...40-241,243-244 
  ignore.ts        |     100 |      100 |     100 |     100 |                   
  result-cache.ts  |     100 |     92.3 |     100 |     100 | 46                
 ...uest-tokenizer |   56.63 |    74.52 |   74.19 |   56.63 |                   
  ...eTokenizer.ts |   41.86 |    76.47 |   69.23 |   41.86 | ...70-443,453-507 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tTokenizer.ts |   68.39 |    69.49 |    90.9 |   68.39 | ...24-325,327-328 
  ...ageFormats.ts |      76 |      100 |   33.33 |      76 | 45-48,55-56       
  textTokenizer.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
-------------------|---------|----------|---------|---------|-------------------

For detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run.

…d, recordCompletedToolCall, consumePendingMemoryTaskPromises)
@LaZzyMan LaZzyMan requested a review from wenshao April 27, 2026 12:08
Comment thread packages/core/src/memory/manager.ts Outdated
Comment thread packages/core/src/memory/skillReviewAgentPlanner.ts Outdated
- Fix merged_with_extract silent drop: remove broken merge optimization
  in scheduleSkillReview(). When an extract task is pending/running,
  skill review is now scheduled independently instead of recording
  metadata that no production code ever reads.

- Fix SKILL_MANAGE blocked from skill review agent: prepareTools() now
  only enforces the recursion guard (AGENT tool) when the agent has an
  explicit tools list. Wildcard/inherit subagents still get the full
  EXCLUDED_TOOLS_FOR_SUBAGENTS filter, preventing task subagents from
  calling skill_manage. The dedicated skill review agent can now receive
  the skill_manage tool it requires.

- Update manager.test.ts: replace merged_with_extract tests with
  concurrent-extract independent-scheduling tests.
- Update skill-manage.test.ts: clarify test description to reflect
  wildcard-only exclusion semantics.
@LaZzyMan LaZzyMan requested a review from wenshao April 28, 2026 06:47
Comment thread packages/core/src/tools/skill-manage.ts Outdated
assertProjectSkillPath() uses path.resolve() which is purely lexical and
does not dereference symlinks. If any path component inside .qwen/skills/
is a symlink pointing outside the project, fs.writeFile/readFile/rm would
follow the link and mutate files outside the advertised write boundary.

Add assertRealProjectSkillPath() (async) in skill-paths.ts that:
- Resolves the real path of the skills root via fs.realpath()
- Walks up from targetPath to find the nearest existing ancestor
- Resolves that ancestor to its real filesystem path
- Rejects if the real path falls outside the real skills root

skill-manage.ts execute() now calls both the cheap lexical check (fast
fail for obviously wrong paths) and the async real-path check before any
fs.writeFile / fs.rm mutation.

Add three symlink-specific tests in skill-paths.test.ts covering:
- Legitimate path accepted
- Symlinked directory pointing outside skills root rejected
- Skills root itself being a symlink (safe target) accepted
Comment thread packages/core/src/memory/skillReviewAgentPlanner.ts Outdated
…rite detection

Address reviewer feedback: instead of keeping skill_manage as the sole
write gate (which still had symlink bypass risk via generic tools), remove
the dedicated tool entirely and replace with a two-layer protection:

1. skillsModifiedInSession (client.ts): detects writes to .qwen/skills/
   by inspecting the file_path arg of every completed tool call, replacing
   the fragile historyCallsSkillManage() history scan.

2. hasAutoSkillSource + evaluateScopedDecision (skillReviewAgentPlanner.ts):
   the review agent's permission sandbox now verifies BOTH that the target
   path is inside the skills directory AND that the existing file already
   contains 'source: auto-skill' in its frontmatter before allowing edits,
   preventing the agent from overwriting user-managed skills.

Changes:
- Delete skill-manage.ts and skill-manage.test.ts
- Remove SKILL_MANAGE from ToolNames, ToolDisplayNames, config registerLazy,
  agent-core EXCLUDED_TOOLS comment, and agent.ts comment
- Replace historyCallsSkillManage() with skillsModified: boolean param in
  scheduleSkillReview; skip reason renamed skills_modified_in_session
- recordCompletedToolCall(name, filePath?) detects .qwen/skills/ writes;
  CLI layers pass file_path arg from tool call request
- Fix buildTaskPrompt frontmatter template to use top-level source: auto-skill
- Update skill-paths.ts error messages to remove skill_manage references
- Update all unit/integration tests accordingly
Comment thread packages/core/src/memory/manager.ts
…Root

scheduleSkillReview() was launching a new background task every time the
threshold was reached for the same project, with no guard against multiple
in-flight reviews running concurrently.

Fix: add skillReviewInFlightByProject Map that tracks the taskId of any
running review per projectRoot. A second call while one is in-flight returns
{ status: 'skipped', skippedReason: 'already_running', taskId: <existing> }.
The map entry is cleared in a finally block inside runSkillReview() so the
next session can schedule a fresh review after the current one completes.

Also extend SkillReviewScheduleResult.skippedReason union to include
'already_running', and add a unit test covering the full lifecycle:
first call schedules, second call is skipped with existing taskId, and a
third call after completion schedules a new task.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: CI is currently failing on all 9 Test jobs (ubuntu/windows/macos × node 20/22/24). One of the integration tests in this PR is itself the cause — see the inline comment on skillReviewNudge.integration.test.ts:279. — claude-opus-4-7 via Qwen Code /review

}
case ToolNames.EDIT:
case ToolNames.WRITE_FILE: {
if (!ctx.filePath || !isProjectSkillPath(ctx.filePath, projectRoot)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] This is the same lexical-only check the previous reviewer flagged — assertRealProjectSkillPath (the symlink-aware async guard added in 60fbe2c) is never called from production code. grep -rn assertRealProjectSkillPath packages/core/src --include='*.ts' | grep -v '\.test\.' returns only the definition site in skill-paths.ts:48. Both client.ts:612 and the gates here at :100 and :260 use lexical isProjectSkillPath, which path.resolves but does not dereference symlinks. FileSystemService.writeTextFile calls plain fs.writeFile — symlinks are followed.

Concrete attack flow (the new-file edge case):

  1. Project ships .qwen/skills/foo.md as a dangling symlink to ~/.zshrc (target doesn't exist yet).
  2. .qwen/settings.json ships with {"memory":{"enableAutoSkill": true}} — once the user trusts the folder, this is honored.
  3. After a single 20+ tool-call turn, the auto-skill agent picks foo.md to write.
  4. hasAutoSkillSource(filePath) follows the symlink → ENOENT → returns null → permission 'allow' ("file doesn't exist, OK to create").
  5. fs.writeFile follows the symlink and creates ~/.zshrc with the model's skill-body content. The user sees a friendly "Skill review updated 1 file(s)" toast.

Also missing: assertRealProjectSkillPath itself doesn't verify the resolved skills root sits inside the project — .qwen/skills symlinked to / would pass the containment check (every absolute path starts with /).

Fix:

  1. Replace isProjectSkillPath here (and at :260 and in client.ts:612) with await assertRealProjectSkillPath(ctx.filePath, projectRoot) wrapped in try/catch (catch → return 'deny').
  2. Add an early check in assertRealProjectSkillPath that realpath(skillsRoot) is inside realpath(projectRoot).
  3. To close the inevitable TOCTOU between check and write, open the file with fs.constants.O_NOFOLLOW (or write to a temp file in the same dir then rename(2)).

— claude-opus-4-7 via Qwen Code /review

ctx: PermissionCheckContext,
projectRoot: string,
): Promise<PermissionDecision> {
switch (ctx.toolName) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] Two scoping holes in evaluateScopedDecision:

1. READ_FILE / LS are in the agent's allowlist (:242-248) but evaluateScopedDecision only handles SHELL/EDIT/WRITE_FILE — read tools fall to 'default', which the base PermissionManager allows for project paths and (under YOLO mode forced by runForkedAgent) auto-allows for outside-workspace paths too. The auto-skill agent inherits the parent session's full conversation history. A hostile user prompt or malicious tool-result text in that history can instruct the agent to read_file ~/.aws/credentials and embed the contents inside the SKILL.md it writes to .qwen/skills/. That file is in the user's project — which is often committed to git. Net: read-and-exfiltrate via auto-generated skill markdown.

2. SHELL is gated only by isShellCommandReadOnlyAST at :89-97. AST-based read-only classification is fragile against process substitution (<(cmd)), command substitution ($(rm x)), eval, here-docs writing to fd, etc. For an unsupervised background agent that doesn't actually need shell, the conservative posture is to deny SHELL entirelyread_file / ls cover the system-prompt's stated use case ("inspect existing skills before writing").

Fix:

  • Add READ_FILE/LS cases to evaluateScopedDecision: only 'allow' when ctx.filePath resolves inside projectRoot (real-path); 'deny' otherwise.
  • Drop SHELL from the tool list at :242-248. If a specific shell command is needed later (ls -la), expose it through an explicit allowlist not the AST.

— claude-opus-4-7 via Qwen Code /review

* `source: auto-skill` in its YAML frontmatter block.
* Returns false if the file does not exist (caller may allow creation).
*/
async function hasAutoSkillSource(filePath: string): Promise<boolean | null> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] hasAutoSkillSource has three issues that combine into a containment bypass for new files:

1. Error swallowing too broad. The catch returns null for any fs.readFile error (ENOENT / EISDIR / EACCES / EMFILE / EPERM). Only ENOENT means "file does not exist; safe to create." The others mean "I can't tell" — narrowing them to 'allow' is fail-open.

2. New-file path allows any content. When null is returned, evaluateScopedDecision emits 'allow' regardless of what the agent is about to write. A model (steered by prompt injection in the parent session) can create new files in .qwen/skills/ without including source: auto-skill, permanently labeling the file as user-authored and immune to future auto-skill updates. The pre-write gate validates the on-disk content, not the proposed content; the protection relies entirely on the system prompt convincing the model to add the marker.

3. Regex /m flag matches frontmatter mid-file. /^---\r?\n([\s\S]*?)\r?\n---/m — with m, ^ matches the start of any line. A user-written skill that contains a ---\nsource: auto-skill\n--- block anywhere (e.g., inside a markdown example demonstrating the feature) is classified auto-managed and editable.

Fix:

} catch (err) {
  if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;
  return false; // any other read error → deny
}

Drop the m flag (or anchor the match to byte 0). For new files, also require ctx.toolParams?.content (plumbed through PermissionCheckContext) to contain the marker — or post-write hook to roll back skill files lacking the marker.

— claude-opus-4-7 via Qwen Code /review

// Both should be processed independently
expect(result1.taskId).toBeDefined();
expect(result2.taskId).toBeDefined();
expect(result1.taskId).not.toBe(result2.taskId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] This test is currently failing on the PR branch:

× Test 5 > should handle multiple skill reviews for same project
  → expected 'cf1566...' not to be 'cf1566...' // Object.is equality

The in-flight dedupe added by f7601a0 makes the second scheduleSkillReview for the same project return the same taskId (with skippedReason: 'already_running'), but this test asserts expect(result1.taskId).not.toBe(result2.taskId) — the inverse. The test was written before the dedupe fix and never updated. As a result CI is red, and any future regression in dedupe would be masked because this test currently fails for an unrelated reason.

Fix: Update to assert result2.taskId === result1.taskId and result2.skippedReason === 'already_running'. Or delete this test — manager.test.ts:278 already covers the behavior correctly.

— claude-opus-4-7 via Qwen Code /review

Comment thread packages/core/src/core/client.ts Outdated
return promises;
}

recordCompletedToolCall(toolName: string, filePath?: string): void {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] recordCompletedToolCall recognizes only 'write_file'/'edit' and only inspects args.file_path. The sibling partWritesToMemory in manager.ts:195-200,237-248 (used to gate the extract task) recognizes 4 tool names (write_file, edit, replace, create_file) and 3 arg keys (file_path, path, target_file). The two sit ~370 lines apart in the same conceptual subsystem.

Concrete failure: A replace tool call (legacy alias for edit per tool-names.ts) that writes to .qwen/skills/foo/SKILL.md will not flip skillsModifiedInSession. The session's auto-skill agent then runs over a session that just wrote skills — exactly the scenario the flag was designed to suppress. Same for any future tool that uses path or target_file (e.g., MCP file-write servers).

This is also a test-coverage gap: no client.test.ts case asserts that recordCompletedToolCall('write_file', '<projectRoot>/.qwen/skills/foo.md') flips the flag.

Fix: Extract a shared extractWrittenFilePath(toolName, args): string | undefined helper used by both call sites. Use the ToolNames.WRITE_FILE/ToolNames.EDIT constants instead of raw strings (the file already imports ToolNames).

— claude-opus-4-7 via Qwen Code /review

Comment thread packages/core/src/core/client.ts Outdated
toolCallCount: this.toolCallCount,
skillsModified: this.skillsModifiedInSession,
enabled: autoSkillEnabled,
threshold: 20,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] threshold: 20, maxTurns: 8, timeoutMs: 120_000 are hardcoded literals at this call site, shadowing the named constants that are exported from manager.ts:187 (AUTO_SKILL_THRESHOLD) and skillReviewAgentPlanner.ts:26-27 (DEFAULT_AUTO_SKILL_MAX_TURNS, DEFAULT_AUTO_SKILL_TIMEOUT_MS). A maintainer who tunes AUTO_SKILL_THRESHOLD and runs the test suite (which uses the constant directly) sees green tests but ships a behavior bug because production reads the literal here.

Also enabled: autoSkillEnabled is dead — the call site is already inside if (autoSkillEnabled) { ... } two lines above, so this field is always true at this point.

Fix: Drop the four explicit fields and let scheduleSkillReview use its built-in defaults; or import the constants and pass them by reference. Either way, do not mix.

— claude-opus-4-7 via Qwen Code /review

Comment thread packages/core/src/core/client.ts Outdated
);
}
}
this.toolCallCount = 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] toolCallCount = 0 and skillsModifiedInSession = false are reset every turn (at the end of runManagedAutoMemoryBackgroundTasks, which fires per UserQuery/ToolResult). The PR description and design doc (docs/design/skill-nudge/skill-nudge.md) explicitly say the counter is session-scoped and only resets at session start. The implementation makes it turn-scoped.

Two observable consequences:

  1. A single 20-tool-call turn fires immediately (matches the threshold, but feels surprising while the user is still in the session).
  2. A multi-turn session totaling 200 tool calls (none ≥ 20 in a single turn) never fires — contrary to the design intent.

Also, skillsModifiedInSession reset semantics: if the user edits a skill file in turn N (the gate at runManagedAutoMemoryBackgroundTasks skips that turn but then resets the flag), then turn N+1 with 20 unrelated tool calls will fire a review even though the user just hand-modified a skill.

Fix: Either (a) move the reset to a real session-end / /clear hook to match the design, or (b) update the design doc to per-turn semantics and rename the field to skillsModifiedThisTurn. Whichever the team picks, pin it in a test.

— claude-opus-4-7 via Qwen Code /review

'Managed skill review completed without durable changes.',
metadata: { touchedSkillFiles: result.touchedSkillFiles },
});
} catch (error) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Two divergences from the sibling runExtract path (and a debuggability gap):

1. Silent failure. runExtract (lines 600-667) rethrows after marking the record failed and emits a MemoryExtractEvent telemetry event for both completed and failed cases. runSkillReview swallows the error inside its catch (records record.error, never rethrows, never emits telemetry). Consumers awaiting skillReviewResult.promise can only learn about a failure by inspecting record.status === 'failed' — the promise never rejects.

2. No telemetry. There is no SkillReviewEvent analogous to MemoryExtractEvent. Combined with: no debugLogger calls in skillReviewAgentPlanner.ts (compare with how Step 3's deterministic tools log via debugLogger), no Footer.tsx indicator (only dream is shown there), no "Generated by Qwen Code skill review" marker in the SKILL.md body itself — when an autoSkill bug fires in production, the operator has no forensic trail: no session ID, no transcript, no agent prompt, nothing identifying the file as auto-generated except a regex-matched frontmatter field.

Fix:

  • Pick one error-handling style. If "swallow" is intentional, add a comment here pointing readers to inspect record.status.
  • Add a minimal MemorySkillReviewEvent (or reuse logMemoryExtract with a taskType: 'skill-review' discriminator) so failures are observable.
  • Embed a <!-- Generated by Qwen Code skill review at <sessionId>/<timestamp> --> HTML comment at the top of every auto-generated SKILL.md so files are identifiable even if frontmatter is hand-edited away.
  • Surface skill-review status in Footer.tsx next to dream.

— claude-opus-4-7 via Qwen Code /review

1. hasAutoSkillSource: narrow catch to ENOENT only (EISDIR/EACCES etc.
   return false to deny); tighten frontmatter regex to match opening block only.

2. evaluateScopedDecision: add explicit allow for READ_FILE and LS so they
   don't fall to 'default' which the base PermissionManager might widen;
   EDIT/WRITE_FILE now call assertRealProjectSkillPath() (async realpath guard)
   in addition to the lexical check, closing the symlink traversal hole.

3. isScopedTool / getScopedDenyRule: cover READ_FILE and LS so hasRelevantRules
   returns true and findMatchingDenyRule is correctly consulted for them.

4. recordCompletedToolCall (client.ts): broaden tool name set to match
   WRITE_TOOL_NAMES in manager.ts (write_file, edit, replace, create_file) and
   inspect all three arg keys (file_path, path, target_file). Signature changed
   from (name, filePath?) to (name, args?) to carry all args through.

5. client.ts hardcoded literals: replace threshold/maxTurns/timeoutMs with the
   named constants AUTO_SKILL_THRESHOLD / DEFAULT_AUTO_SKILL_MAX_TURNS /
   DEFAULT_AUTO_SKILL_TIMEOUT_MS imported from manager.ts and
   skillReviewAgentPlanner.ts.

6. toolCallCount / skillsModifiedInSession reset: only reset when skill review
   is actually scheduled (status === 'scheduled'), not every turn, so the
   counter correctly accumulates across turns within a session as per design doc.

7. runSkillReview (manager.ts): rethrow after marking record failed, consistent
   with runExtract behavior.

8. skillReviewNudge.integration.test.ts test 5: rewrite to reflect the
   in-flight dedup contract (second same-project call returns already_running
   with existing taskId; third call after completion gets a new task). Add
   vi.mock for runSkillReviewByAgent so the test does not need a full Config.
Comment thread packages/core/src/core/client.ts Outdated
this.toolCallCount = 0;
this.skillsModifiedInSession = false;
if (skillReviewResult.promise) {
this.pendingMemoryTaskPromises.push(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] runSkillReview() now rethrows background-agent failures, but this promise is pushed with only .then(...). In the interactive path, useGeminiStream also consumes pending memory-task promises with only void p.then(...), so an autoSkill timeout/API/permission failure can surface as an unhandled rejection instead of being treated as best-effort background work like extract/dream tasks.

Suggested change
this.pendingMemoryTaskPromises.push(
this.pendingMemoryTaskPromises.push(
skillReviewResult.promise
.then((record) => {
const touched = record.metadata?.['touchedSkillFiles'];
return Array.isArray(touched) ? touched.length : 0;
})
.catch((error: unknown) => {
debugLogger.warn(
'Failed to run managed skill review.',
error,
);
return 0;
}),
);

— gpt-5.5 via Qwen Code /review

case ToolNames.LS:
// Read tools are always allowed — the agent needs to inspect skills.
return 'allow';
case ToolNames.SHELL: {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] This allows any shell command classified as read-only without constraining the command's cwd or referenced paths to .qwen/skills. Even if read_file / list_directory are scoped separately, the background skill-review agent can still read arbitrary local files through commands like cat, grep, find, or ls, which bypasses the intended project-skills-only inspection model.

Either remove ToolNames.SHELL from the skill-review agent tools, or make this approval path-aware and realpath-validated under getProjectSkillsRoot(projectRoot) before returning allow.

— gpt-5.5 via Qwen Code /review

// Resolve the real path of the skills root (it may itself be a symlink).
let realSkillsRoot: string;
try {
realSkillsRoot = await fs.realpath(skillsRoot);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] A dangling symlink at the final target path is still treated as a safe missing file. For example, if .qwen/skills/foo/SKILL.md is an existing symlink to /tmp/pwned-skill.md whose target does not exist, fs.realpath(Skill.md) throws ENOENT, this helper walks to the parent and returns success, and hasAutoSkillSource() also treats readFile ENOENT as creation. write_file then follows the symlink and writes outside the skills root.

Before treating ENOENT as safe, lstat() the current path; if the path itself exists and is a symlink, deny it or resolve readlink() and verify the resolved target stays under the real skills root. Also only let hasAutoSkillSource() ENOENT mean creation after the symlink-aware check proves the final path is not an existing symlink.

— gpt-5.5 via Qwen Code /review

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No-unit-test gaps (can't map to diff lines):

  • packages/core/src/memory/skillReviewAgentPlanner.ts (294 lines): entire file has no dedicated unit tests. Permission sandbox logic (evaluateScopedDecision, hasAutoSkillSource, mergePermissionDecision) is untested.
  • packages/core/src/core/client.ts:617-641 (recordCompletedToolCall): tool counting and skillsModifiedInSession detection logic has no direct unit tests.

— deepseek-v4-pro via Qwen Code /review

Comment thread packages/core/src/core/client.ts Outdated

if (!this.config.getManagedAutoMemoryEnabled()) {
if (
messageType !== SendMessageType.UserQuery &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] runManagedAutoMemoryBackgroundTasks gate expanded from UserQuery-only to UserQuery || ToolResult, but the gate change applies to the entire method — not just the autoSkill block. This causes scheduleExtract() and scheduleDream() to fire on every tool result submission, not just on user-initiated turns.

Each call triggers getHistory() (deep clone of entire conversation) and file I/O checks. A session with 20 tool calls now triggers 20 extract+dream attempts instead of 1.

Suggested change
messageType !== SendMessageType.UserQuery &&
private runManagedAutoMemoryBackgroundTasks(
messageType: SendMessageType,
): void {
// autoSkill: can trigger on UserQuery or ToolResult
if (
messageType === SendMessageType.UserQuery ||
messageType === SendMessageType.ToolResult
) {
if (this.config.getAutoSkillEnabled()) {
// ... scheduleSkillReview logic (existing)
}
}
// extract/dream: keep UserQuery-only to preserve existing behavior
if (messageType !== SendMessageType.UserQuery) return;
if (!this.config.getManagedAutoMemoryEnabled()) return;
// ... scheduleExtract, scheduleDream (existing)
}

— deepseek-v4-pro via Qwen Code /review

Comment thread packages/core/src/core/client.ts Outdated
timeoutMs: DEFAULT_AUTO_SKILL_TIMEOUT_MS,
});
if (skillReviewResult.status === 'scheduled') {
// Reset per-session counters only when review is actually dispatched,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] skillsModifiedInSession is an irreversible deadlock: once set to true by any write to .qwen/skills/, it can never be reset. The reset (this.skillsModifiedInSession = false at line 546) is inside if (skillReviewResult.status === 'scheduled'), but scheduleSkillReview() checks skillsModified before reaching the scheduling path and returns 'skipped' immediately. One skill write permanently disables autoSkill for the entire session.

Suggested change
// Reset per-session counters only when review is actually dispatched,
if (skillReviewResult.status === 'scheduled') {
this.toolCallCount = 0;
if (skillReviewResult.promise) {
this.pendingMemoryTaskPromises.push(
skillReviewResult.promise.then((record) => {
const touched = record.metadata?.['touchedSkillFiles'];
return Array.isArray(touched) ? touched.length : 0;
}).catch((error: unknown) => {
debugLogger.warn('Failed to run managed skill review.', error);
return 0;
}),
);
}
}
// Always reset to avoid deadlock — any write to skills dir in this
// turn was already captured by scheduleSkillReview's check above.
this.skillsModifiedInSession = false;

— deepseek-v4-pro via Qwen Code /review

Comment thread packages/core/src/config/config.ts Outdated
enableManagedAutoMemory?: boolean;
/** Enable managed auto-dream consolidation separately from extraction. Defaults to true. */
enableManagedAutoDream?: boolean;
/** Enable automatic project skill review after tool-heavy sessions. Defaults to true. */

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] JSDoc says Defaults to true. but the Config constructor (this.enableAutoSkill = params.enableAutoSkill ?? false), settingsSchema.ts (default: false), and loadCliConfig all default to false. The PR description also says「默认关闭」. The JSDoc is misleading.

Suggested change
/** Enable automatic project skill review after tool-heavy sessions. Defaults to true. */
/** Enable automatic project skill review after tool-heavy sessions. Defaults to false. */
enableAutoSkill?: boolean;

— deepseek-v4-pro via Qwen Code /review

Comment thread packages/core/src/core/client.ts Outdated
toolName: string,
args?: Record<string, unknown>,
): void {
const SKILL_WRITE_TOOL_NAMES = new Set([

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Two issues with the write-tool detection: (1) SKILL_WRITE_TOOL_NAMES is a new Set() allocated on every recordCompletedToolCall invocation. (2) It contains 'replace' and 'create_file' which don't exist in QwenCode's tool registry — likely copy-paste residue. manager.ts:200 already has a module-level WRITE_TOOL_NAMES constant with correct values. Also, run_shell_command with redirects (e.g. echo ... > .qwen/skills/evil/SKILL.md) bypasses this detection entirely.

Suggested change
const SKILL_WRITE_TOOL_NAMES = new Set([
// Module-level (outside method):
const SKILL_WRITE_TOOL_NAMES: ReadonlySet<string> = new Set([
'write_file',
'edit',
]);

— deepseek-v4-pro via Qwen Code /review

},
});

const promise = this.track(record.id, this.runSkillReview(record, params));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] runSkillReview() has no telemetry at all. Contrast with runExtract() which emits MemoryExtractEvent on both success and failure paths. Without telemetry, there's no way to monitor skill review reliability or effectiveness in production.

Consider adding a telemetry event recording status, touchedSkillFiles, and duration_ms.

— deepseek-v4-pro via Qwen Code /review

const skillsRoot = path.resolve(getProjectSkillsRoot(projectRoot));
const resolved = path.resolve(filePath);
return resolved === skillsRoot || resolved.startsWith(skillsRoot + path.sep);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] isProjectSkillPath calls path.resolve(filePath) which resolves relative paths against process.cwd(), not projectRoot. In multi-root workspaces or when cwd differs from project root, a relative filePath could resolve outside the skills containment check.

Suggested change
}
const resolved = path.resolve(projectRoot, filePath);

— deepseek-v4-pro via Qwen Code /review

);
} else {
// Explicit tool list: only prevent recursive agent spawning.
toolsList.push(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] For subagents with explicit tool lists, the exclusion set was narrowed from EXCLUDED_TOOLS_FOR_SUBAGENTS (blocks AGENT, CRON_CREATE, CRON_LIST, CRON_DELETE, TASK_STOP, SEND_MESSAGE) to just recursionGuardOnly (only AGENT). This means control-plane tools like TASK_STOP and CRON_CREATE could leak to explicitly-configured subagents. While the skill review agent doesn't declare these, this weakens the safety net for future subagent configs.

Consider restoring the full exclusion or documenting the rationale.

— deepseek-v4-pro via Qwen Code /review

}
this.toolCallCount += 1;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] toolCallCount increments for all completed tool calls, including read-only operations (list_directory, read_file, grep_search). A simple "read one file and edit one line" task can produce 5-10 tool calls. In practice, the threshold of 20 is reached much sooner than the design doc's intended "tool-heavy session" semantics suggest.

Consider weighting write operations higher, or counting only non-read-only tools.

— deepseek-v4-pro via Qwen Code /review

@wenshao

wenshao commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Code Review

Overview

Adds a background "skill review" agent that runs after tool-heavy sessions (≥ 20 tool calls) to extract reusable procedural skills into ${projectRoot}/.qwen/skills/. Default-off behind memory.enableAutoSkill. Most of the surface is new (skillReviewAgentPlanner.ts, skill-paths.ts, MemoryManager.scheduleSkillReview), but a few existing-system touches deserve scrutiny.

High-priority issues

1. Silent behavior change to existing scheduleExtract/scheduleDream triggers

packages/core/src/core/client.ts:516-521 widens the gate from UserQuery only to UserQuery || ToolResult. The new condition is needed for skill review (so the threshold can fire mid-session), but scheduleExtract and scheduleDream are also inside the same function and now run on every tool round-trip too. Memory extract has dedup, but with several tool round-trips per turn you'll constantly re-queue trailing extracts and refresh the dream-eligibility check. The PR description doesn't acknowledge this, and there is no test that pins the previous "once per UserQuery" semantics. Either guard the new branch with messageType === ToolResult for skill review only, or call this out explicitly and add coverage.

2. agent-core.ts quietly relaxes EXCLUDED_TOOLS_FOR_SUBAGENTS for explicit tool lists

packages/core/src/agents/runtime/agent-core.ts:347-360: the explicit-tools branch previously filtered the full excluded set (AGENT, CRON_*, TASK_STOP, SEND_MESSAGE); after this PR it filters only AGENT. So any subagent that explicitly lists cron_create/task_stop/send_message in its tool config now gets them — a security-relevant regression unrelated to autoSkill.

The companion change in packages/core/src/tools/agent/agent.ts:706-720 tightens the inherited-tools path (good) but doesn't justify loosening the explicit-tools path. The skill-review agent only uses read_file/ls/shell/write_file/edit, none of which were excluded in the first place. Either revert the agent-core.ts change or move it to its own PR with a security justification.

3. buildAgentHistory silently drops the trailing user message

packages/core/src/memory/skillReviewAgentPlanner.ts:211-214:

if (last.role !== 'model') return history.slice(0, -1);

If the session is checkpointed mid-turn (history ends with a user role), the last user message — likely the most relevant context for skill extraction — is dropped. There's no test for this branch. Either change to return history; or add a comment + test explaining why the trailing user message must be excluded.

4. Counter reset enables multiple reviews per session

packages/core/src/core/client.ts:542-546: after a successful schedule, toolCallCount resets to 0 and skillsModifiedInSession to false. With a long session of 60 tool calls, you'll trigger 3 reviews. The already_running dedup prevents concurrent dupes, but as soon as the first finishes, the next 20 tool calls trigger a fresh one. The PR description and design doc both say "after the session ends," which is misleading. Decide if multi-fire is intentional and document it; otherwise gate on session-end only.

Medium-priority issues

5. hasAutoSkillSource regex doesn't match the design-doc format

packages/core/src/memory/skillReviewAgentPlanner.ts:64-68 uses /^source:\s*auto-skill\s*$/m, which only matches source: at column 0. The design doc (skill-nudge.md L302-309) puts it nested under metadata::

metadata:
  source: auto-skill

A leading-space source: auto-skill will not match — files written following the docs would be denied edits. The agent's task prompt does write top-level source:, so the system is internally consistent, but reconcile docs with implementation.

6. recordCompletedToolCall blindspot for shell writes

packages/core/src/core/client.ts:617-637 only flags skillsModifiedInSession when a tool with name in {write_file, edit, replace, create_file} has args.file_path|path|target_file pointing inside .qwen/skills/. A user run_shell_command doing echo … > .qwen/skills/foo/SKILL.md (or mv/cp) is not detected, so the user's manual skill edits via shell will still trigger a review and possibly compete with the agent. Worth at least noting the limitation; ideally the file watcher already used by SkillManager could be the source of truth instead of args inspection.

Also: 'create_file' isn't in ToolNames/ToolNamesMigration — either dead defense-in-depth or stale future-proofing.

7. findMatchingDenyRule returns rule strings even when the operation is allowed

packages/core/src/memory/skillReviewAgentPlanner.ts:138-155: getScopedDenyRule returns a rule message for SHELL/EDIT/WRITE_FILE regardless of whether the call would actually be denied. Callers that interpret a non-undefined rule as "this is being denied" will be misled. The function should return undefined when evaluate(ctx) would return allow, or it should be renamed (e.g., getScopedRuleDescription).

8. Comment/code mismatch on default

packages/core/src/config/config.ts:456 — the JSDoc says "Defaults to true" but params.enableAutoSkill ?? false and the schema both default to false. Fix the comment.

9. Object.create(config) as Config is fragile

packages/core/src/memory/skillReviewAgentPlanner.ts:189: the prototype-based override only works because Config uses TS-private, not ECMAScript #private. If anyone migrates a single field to #, the scoped agent silently breaks at runtime. Consider a thin wrapper class or a real proxy.

Minor / style

  • assertRealProjectSkillPath TOCTOU: realpath check then write is racy if a symlink is swapped in between. Low risk for a local CLI but worth a comment.
  • Path leakage in deny messages (skillReviewAgentPlanner.ts:149-151): full absolute project path appears in the rule string returned to the LLM. Fine for now, but it's the kind of thing telemetry/eval logs sometimes capture.
  • tasksCommand.ts formatting-only change and the task-stop.test.ts reformat have nothing to do with autoSkill — should be in a separate cleanup commit.
  • Hard-coded constants (AUTO_SKILL_THRESHOLD = 20, DEFAULT_AUTO_SKILL_MAX_TURNS = 8, DEFAULT_AUTO_SKILL_TIMEOUT_MS = 120_000) are spread across two files (manager.ts and skillReviewAgentPlanner.ts); colocate or re-export to keep one source of truth.

Tests

  • 21 unit tests + 18 integration tests for the manager surface; threshold/disable/skills-modified/already-running paths are well covered.
  • runSkillReviewByAgent itself is mocked everywhere — no test exercises evaluateScopedDecision, the real frontmatter regex against a real file, or the Object.create config proxy. Add a unit test that calls evaluateScopedDecision with each tool/path combo (existing auto-skill, existing user skill, non-existent path, symlink). The auto-skill marker is the central security claim of the PR; please test it directly.
  • No test verifies the messageType === ToolResult branch (pre-release: fix ci #1) or buildAgentHistory's three branches (如何自定义密钥文件 .env可能与其他文件冲突 #3).

Security

  • READ_FILE is unrestricted by design — the review agent can read any file on disk and send it to the model. Documented in the design doc; flag for users in release notes.
  • SHELL read-only via AST: isShellCommandReadOnlyAST blocks redirections but presumably still allows network exfiltration tools (curl, wget). Out of scope for this PR but worth a follow-up.
  • No notification to user when a review runs: the only signal is files appearing in .qwen/skills/. Consider surfacing it in /tasks or a startup banner so users discover auto-generated skills.

Bottom line

Core feature is well-structured and the path/permission scoping is thoughtful. Two changes need to be revisited before merge:

  1. The agent-core.ts excluded-tools loosening (Where is the config saved? #2) — split or revert.
  2. The runManagedAutoMemoryBackgroundTasks gate widening (pre-release: fix ci #1) — confirm intent or scope the new branch to skill review only.

Plus the buildAgentHistory last-user-message drop (#3) and the regex/doc mismatch (#5) should be fixed; the rest are polish.

LaZzyMan added 2 commits May 8, 2026 17:40
- skill-paths: detect dangling symlinks with lstat before treating ENOENT as safe
- skill-paths: fix isProjectSkillPath relative path resolution to use projectRoot
- skillReviewAgentPlanner: restrict READ_FILE/LS to project root only
- skillReviewAgentPlanner: remove SHELL tool from review agent tool list
- skillReviewAgentPlanner: add path import; remove unused shell imports
- skillReviewAgentPlanner: add comment for buildAgentHistory trailing user message
- client: fix runManagedAutoMemoryBackgroundTasks gate widening
- client: fix skillsModifiedInSession deadlock
- client: add .catch() to skill review promise
- client: hoist SKILL_WRITE_TOOL_NAMES to module-level ReadonlySet
- agent-core: use full EXCLUDED_TOOLS_FOR_SUBAGENTS for explicit tool list subagents
- manager: extend notify() signature to accept 'skill-review' taskType
- config: fix JSDoc default value comment (false, not true)

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ CI failing: Test (windows-latest, Node 22.x). 发现 6 个新增 Critical 问题,主要是测试覆盖缺口:

  1. autoSkill 分支完全未经测试client.test.tsgetAutoSkillEnabled 始终 mock 为 falserunManagedAutoMemoryBackgroundTasks 的整个 autoSkill 路径(调度、threshold 检查、promise 处理、计数器重置)从未被执行。
  2. recordCompletedToolCall 完全未经测试 — 方法有零测试覆盖率。skillsModifiedInSession 的唯一设置机制未经验证。
  3. nonInteractiveCli.ts 新增调用无断言recordCompletedToolCallconsumePendingMemoryTaskPromises 有 mock 定义但无断言验证调用。
  4. useGeminiStream.ts 新增循环无断言recordCompletedToolCall 的 for 循环有 mock 无断言,TUI 模式下 tool 计数无效。
  5. scheduleSkillReview 去重粒度与 scheduleExtract 不一致 — extract 排队尾随请求,skill-review 直接丢弃。若审查飞行期间再次达到阈值,后续调用被静默跳过且 toolCallCount 不重置。

— DeepSeek/deepseek-v4-pro via Qwen Code /review

@@ -148,6 +148,8 @@ describe('runNonInteractive', () => {

mockGeminiClient = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] TS2353: consumePendingMemoryTaskPromises 在 mock 对象类型中不存在。GeminiClient 新增了此方法(第 762 行),但 mock 类型未包含它,导致 tsc --noEmit 编译失败。

Suggested change
mockGeminiClient = {
consumePendingMemoryTaskPromises: vi.fn().mockReturnValue([]),
recordCompletedToolCall: vi.fn(),

— DeepSeek/deepseek-v4-pro via Qwen Code /review

@@ -166,6 +192,8 @@ export interface DrainOptions {

export const EXTRACT_TASK_TYPE = 'managed-auto-memory-extraction' as const;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] SKILL_REVIEW_TASK_TYPE = 'managed-skill-extractor' 被导出但从未使用。实际的 makeTaskRecord 调用使用字面量 'skill-review'。两者值不同——要么常量的值错误,要么 record 创建错误地绕过了常量。

— DeepSeek/deepseek-v4-pro via Qwen Code /review

messageType === SendMessageType.UserQuery ||
messageType === SendMessageType.ToolResult
) {
const projectRoot = this.config.getProjectRoot();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] this.getHistory()(含 structuredClone 深拷贝)在每个 ToolResult 轮次中被无条件调用,即使 autoSkillEnabledfalse(默认值)。将 getHistory() 移入 autoSkillEnabled 守卫内部,避免对使用默认配置的用户造成不必要的 O(n) 性能开销。

Suggested change
const projectRoot = this.config.getProjectRoot();
if (autoSkillEnabled && this.toolCallCount >= AUTO_SKILL_THRESHOLD) {
const history = this.getHistory();
const skillReviewResult = mgr.scheduleSkillReview({
...
});
}

— DeepSeek/deepseek-v4-pro via Qwen Code /review

@@ -444,9 +475,9 @@ export class MemoryManager {
* subscribers can be reached too; the unfiltered subscriber set
* always receives the wakeup either way.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] notify()taskType 参数包含 'skill-review',并通过 taskType !== 'skill-review' 显式跳过 typed subscribers。但 subscribe()taskType 只接受 'extract' | 'dream',不包含 'skill-review'。这是一个无法通过公共 API 到达的代码路径——要么移除跳过守卫,要么扩展 subscribe 以接受 'skill-review'

— DeepSeek/deepseek-v4-pro via Qwen Code /review

if (
resolvedRead === resolvedRoot ||
resolvedRead.startsWith(resolvedRoot + path.sep)
) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] evaluateScopedDecisionREAD_FILELS 的范围是整个项目根目录,而非仅 .qwen/skills/。Skill review agent 可以读取项目中的任意文件(如 .env、配置文件),并将内容嵌入生成的 skill 文件,可能造成密钥泄露。

建议将 READ_FILE 限制为 .qwen/skills/,或至少添加 deny 规则阻止常见密钥文件名。

— DeepSeek/deepseek-v4-pro via Qwen Code /review

const toolsList: FunctionDeclaration[] = [];

const excludedFromSubagents = EXCLUDED_TOOLS_FOR_SUBAGENTS;
// When a subagent has an explicit tools list (not wildcard), only the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] recursionGuardOnly 的注释与实际逻辑矛盾。顶部注释说「When a subagent has an explicit tools list...only the recursive-spawn guard is enforced」,但 else 分支内部却写着「Wildcard tool list...only block recursive spawning」——而这段代码实际执行的是显式工具列表路径。两个注释互相矛盾,会误导未来的维护者。

Suggested change
// When a subagent has an explicit tools list (not wildcard), only the
// Explicit tool list: only block recursive agent spawning.
// Other control-plane tools (cron, task, send_message) are
// allowed since the explicit list already bounds the agent.
const recursionGuardOnly = new Set<string>([ToolNames.AGENT]);

— DeepSeek/deepseek-v4-pro via Qwen Code /review

- client: reset toolCallCount when scheduleSkillReview returns already_running
  and count >= threshold, preventing immediate cascade after in-flight review
- client.test: add autoSkill branch tests (scheduled/already_running/skills_modified)
- client.test: add full recordCompletedToolCall unit tests (skillsModifiedInSession,
  toolCallCount increment, skill path detection for write_file/edit/read_file)
- client.test: add scheduleSkillReview mock to mockMemoryManager
- nonInteractiveCli.test: add assertions for recordCompletedToolCall and
  consumePendingMemoryTaskPromises in tool-call integration test
@LaZzyMan

LaZzyMan commented May 9, 2026

Copy link
Copy Markdown
Collaborator Author

感谢所有 review 意见!已在最新两个提交(493e251ba37d83a21d)中解决所有问题,概要如下:

Round 1 修复(493e251ba

问题 处理方式
C1 悬空符号链接绕过 assertRealProjectSkillPath lstat 检测 symlink,在 ENOENT 路径前先拦截
C2 autoSkill 门控宽化(ToolResult 也触发 extract/dream) 拆分为两个独立 if 块,extract/dream 保持 UserQuery only
C3 skillsModifiedInSession 死锁 = false 移到 scheduled 分支外,始终重置
C4 Promise 未捕获异常 添加 .catch() 并记录 debugLogger.warn
C5 subagent 工具列表泄露受限工具 使用完整 EXCLUDED_TOOLS_FOR_SUBAGENTS,不再只过滤 AGENT
S1 READ_FILE/LS 无路径限制 evaluateScopedDecision 中添加 projectRoot containment 检查
S5 isProjectSkillPath 相对路径基准 path.resolve(filePath)path.resolve(projectRoot, filePath)
S7 SHELL 工具 从 autoSkill review agent 工具列表中完全移除
S8 buildAgentHistory 注释缺失 添加说明为何 user role 末尾也需截断
S10 JSDoc 默认值 修正 enableAutoSkill 注释:Defaults to true → false
SKILL_WRITE_TOOL_NAMES 提升为 module-level ReadonlySet,使用 ToolNames 枚举
manager.ts notify() 类型 扩展签名接受 'skill-review' taskType

Round 2 修复(37d83a21d

问题 处理方式
去重粒度 bugalready_runningtoolCallCount 不重置 skippedReason === 'already_running' && count >= threshold 时也重置计数器,防止 in-flight 完成后立即级联触发新 review
autoSkill 分支未测试 新增 4 个测试用例覆盖 scheduled/skipped/already_running/skillsModified 路径
recordCompletedToolCall 未测试 新增 5 个单元测试:计数递增、技能路径检测(write_file/edit)、非写入工具不触发
mockMemoryManager 缺少 scheduleSkillReview 添加默认 mock,所有现有测试正常兼容
nonInteractiveCli.test.ts 无断言 在 tool-call 集成测试中添加 recordCompletedToolCallconsumePendingMemoryTaskPromises 断言

关于 useGeminiStream.ts 覆盖缺口

useGeminiStream 是一个约 2340 行的复杂 Ink React Hook,目前整个文件没有单元测试文件。为该 hook 添加测试需要完整的 Ink/React 渲染环境,超出本次 PR 的范围。已知 issue,后续可独立创建测试。

测试状态

  • ✅ 单元测试:145 个测试通过(skill-pathsmanagerskillReviewNudge.integrationclient
  • ✅ CLI e2e 非 API 测试:18 个通过(settings-migration + json-output
  • ⚠️ 其余 e2e 失败:均为本地无 GEMINI_API_KEY 环境导致的超时,与本 PR 改动无关(所有失败文件均未被本 PR 修改)
  • ⚠️ vscode-ide-companion 测试失败:预先存在于 main 分支,与本 PR 无关(toolcalls/index.test.tsx 最后由 2fea1d3aa 修改,该提交已在 main 上)

1 similar comment
@LaZzyMan

LaZzyMan commented May 9, 2026

Copy link
Copy Markdown
Collaborator Author

感谢所有 review 意见!已在最新两个提交(493e251ba37d83a21d)中解决所有问题,概要如下:

Round 1 修复(493e251ba

问题 处理方式
C1 悬空符号链接绕过 assertRealProjectSkillPath lstat 检测 symlink,在 ENOENT 路径前先拦截
C2 autoSkill 门控宽化(ToolResult 也触发 extract/dream) 拆分为两个独立 if 块,extract/dream 保持 UserQuery only
C3 skillsModifiedInSession 死锁 = false 移到 scheduled 分支外,始终重置
C4 Promise 未捕获异常 添加 .catch() 并记录 debugLogger.warn
C5 subagent 工具列表泄露受限工具 使用完整 EXCLUDED_TOOLS_FOR_SUBAGENTS,不再只过滤 AGENT
S1 READ_FILE/LS 无路径限制 evaluateScopedDecision 中添加 projectRoot containment 检查
S5 isProjectSkillPath 相对路径基准 path.resolve(filePath)path.resolve(projectRoot, filePath)
S7 SHELL 工具 从 autoSkill review agent 工具列表中完全移除
S8 buildAgentHistory 注释缺失 添加说明为何 user role 末尾也需截断
S10 JSDoc 默认值 修正 enableAutoSkill 注释:Defaults to true → false
SKILL_WRITE_TOOL_NAMES 提升为 module-level ReadonlySet,使用 ToolNames 枚举
manager.ts notify() 类型 扩展签名接受 'skill-review' taskType

Round 2 修复(37d83a21d

问题 处理方式
去重粒度 bugalready_runningtoolCallCount 不重置 skippedReason === 'already_running' && count >= threshold 时也重置计数器,防止 in-flight 完成后立即级联触发新 review
autoSkill 分支未测试 新增 4 个测试用例覆盖 scheduled/skipped/already_running/skillsModified 路径
recordCompletedToolCall 未测试 新增 5 个单元测试:计数递增、技能路径检测(write_file/edit)、非写入工具不触发
mockMemoryManager 缺少 scheduleSkillReview 添加默认 mock,所有现有测试正常兼容
nonInteractiveCli.test.ts 无断言 在 tool-call 集成测试中添加 recordCompletedToolCallconsumePendingMemoryTaskPromises 断言

关于 useGeminiStream.ts 覆盖缺口

useGeminiStream 是一个约 2340 行的复杂 Ink React Hook,目前整个文件没有单元测试文件。为该 hook 添加测试需要完整的 Ink/React 渲染环境,超出本次 PR 的范围。已知 issue,后续可独立创建测试。

测试状态

  • ✅ 单元测试:145 个测试通过(skill-pathsmanagerskillReviewNudge.integrationclient
  • ✅ CLI e2e 非 API 测试:18 个通过(settings-migration + json-output
  • ⚠️ 其余 e2e 失败:均为本地无 GEMINI_API_KEY 环境导致的超时,与本 PR 改动无关(所有失败文件均未被本 PR 修改)
  • ⚠️ vscode-ide-companion 测试失败:预先存在于 main 分支,与本 PR 无关(toolcalls/index.test.tsx 最后由 2fea1d3aa 修改,该提交已在 main 上)

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

wenshao
wenshao previously requested changes May 9, 2026

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical — test coverage gaps (无法映射到具体 diff 行)

C1. packages/core/src/core/client.test.tsrunManagedAutoMemoryBackgroundTasksToolResult 触发路径零测试覆盖。autoSkill 块的门控已扩展为 UserQuery || ToolResult(代码注释明确说明"so the threshold can fire mid-session"),但所有 autoSkill 测试仅使用 UserQuery 路径。阈值跨过的关键场景(工具执行中达到 20 次)依赖 ToolResult 路径触发,若有 bug 无法被发现。

C2. packages/core/src/memory/manager.test.tsrunSkillReview 的 catch/error 路径零测试覆盖。runSkillReviewByAgent 始终被 mock 为 resolved,没有任何测试验证 rejection 时 task record 状态正确过渡到 'failed',也没有验证 finally 块中 skillReviewInFlightByProject.delete() 清理是否执行。

S3. packages/cli/src/ui/hooks/useGeminiStream.test.tsxrecordCompletedToolCall 被 mock 为 vi.fn(),但整个测试文件中没有任何断言验证该 mock 被实际调用或传入了正确的工具名和参数。交互式路径中的 toolCallCount 递增和 skillsModifiedInSession 检测完全未经测试。

S4. agent-core.ts / agent.ts — 此 PR 修改了两处子 agent 工具过滤行为(内联 FunctionDeclaration 过滤 + parentToolDecls 过滤),但没有任何回归测试验证这些变更。

realSkillsRoot = await fs.realpath(skillsRoot);
} catch {
// Skills root does not exist yet — nothing to traverse.
return;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] assertRealProjectSkillPath.qwen/skills/ 目录不存在时提前返回(catch 后直接 return),完全跳过 symlink 遍历检测。如果攻击者将 .qwen/ 替换为指向项目外部的 symlink(例如 .qwen/ → /tmp/evil/),随后 review agent 首次写入 skill 文件时会跟随 symlink 写到项目外。symlink 防护恰好覆盖了最需要防护的场景(目录不存在时的首次写入),却在此路径直接放行。

Suggested change
return;
// 不应在 skills root 不存在时直接返回。应向上遍历找到最近的已存在祖先目录,解析其真实路径,验证是否在项目根目录内。
try {
realSkillsRoot = await fs.realpath(skillsRoot);
} catch (err) {
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
// Walk up to find nearest existing ancestor and verify it's within projectRoot
let ancestor = path.dirname(skillsRoot);
while (true) {
try {
const realAncestor = await fs.realpath(ancestor);
const resolvedRoot = path.resolve(projectRoot);
if (realAncestor !== resolvedRoot && !realAncestor.startsWith(resolvedRoot + path.sep)) {
throw new Error(`Skills root ancestor ${ancestor} resolves outside project`);
}
break;
} catch (innerErr) {
if ((innerErr as NodeJS.ErrnoException).code === 'ENOENT') {
const parent = path.dirname(ancestor);
if (parent === ancestor) break;
ancestor = parent;
continue;
}
throw innerErr;
}
}
return;
}
throw err;
}

— DeepSeek/deepseek-v4-pro via Qwen Code /review

},
};

const scopedConfig = Object.create(config) as Config;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] createSkillScopedAgentConfig 使用 Object.create(config) 创建 scoped config,仅覆盖 getPermissionManager,所有其他 Config 方法(getGeminiClient()getModel()getProjectRoot() 等)通过原型链完全继承原始 config,不做任何沙箱限制。如果 runForkedAgent 内部通过 config 访问除 permission manager 之外的任何能力,skill review agent 将获得完整权限。建议创建真正的受限 Config 包装器,显式覆盖所有可能被 fork agent 使用的方法。

— DeepSeek/deepseek-v4-pro via Qwen Code /review

"If nothing is worth saving, just say 'Nothing to save.' and stop.",
].join('\n');

function buildAgentHistory(history: Content[]): Content[] {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] buildAgentHistorylast.role !== 'model' 时无条件 history.slice(0, -1)。如果历史仅包含一条用户消息(headless 单次查询),将产生空数组,且没有任何 warn 日志、遥测事件或跳过原因记录说明为何技能提取未执行。这是零信号的静默故障模式 — 在无头模式下发生崩溃或会话提前终止时,autoSkill 功能彻底失效却无可观测信号。

Suggested change
function buildAgentHistory(history: Content[]): Content[] {
if (last.role !== 'model') {
debugLogger.warn('buildAgentHistory: dropping trailing user turn, review may have no context', { historyLength: history.length });
return history.slice(0, -1);
}

— DeepSeek/deepseek-v4-pro via Qwen Code /review

];
}

function buildTaskPrompt(skillsRoot: string): string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] buildTaskPrompt 生成的 SKILL.md frontmatter 模板只包含 namedescriptionsource: auto-skillextracted_at没有 session_id 字段sessionId 在整个调用链中可用(ScheduleSkillReviewParamsrunSkillReview),但从未传递给 runSkillReviewByAgentbuildTaskPrompt。当生成的 SKILL.md 包含错误指令或幻觉步骤时,只能凭时间戳做弱匹配来追溯源对话,所有磁盘上的会话日志都是候选,无确定性关联。建议在 frontmatter 中增加 session_id,并考虑在 runSkillReview 完成时发出 SkillReviewEvent 遥测事件。

— DeepSeek/deepseek-v4-pro via Qwen Code /review

vi.spyOn(client['config'], 'getProjectRoot').mockReturnValue(
'/project',
);
client.recordCompletedToolCall('edit', {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] edit 工具测试使用 { path: '/project/.qwen/skills/my-skill.md' },但生产环境中 edit 工具使用 file_path 参数名(非 path)。此测试验证的是死回退分支 args['path'],而非真实的 file_path 路径。如果 args['file_path'] 处理逻辑有 bug,此测试无法发现。

Suggested change
client.recordCompletedToolCall('edit', {
client.recordCompletedToolCall('edit', {
file_path: '/project/.qwen/skills/my-skill.md',
});

— DeepSeek/deepseek-v4-pro via Qwen Code /review

}
toolsList.push(...onlyInlineDecls);
// Also filter inline FunctionDeclaration[] passed directly in toolConfig.
toolsList.push(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] 内联 FunctionDeclaration[]onlyInlineDecls)仅按 recursionGuardOnly(只含 AGENT)过滤,而显式字符串工具列表按完整的 excludedFromSubagents(含 CRON_CREATETASK_STOPSEND_MESSAGE 等)过滤。如果通过内联方式传入控制面工具,将绕过子 agent 排除过滤器。建议对 onlyInlineDecls 使用相同的 excludedFromSubagents 集合,保持一致性。

— DeepSeek/deepseek-v4-pro via Qwen Code /review

args?: Record<string, unknown>,
): void {
if (args && SKILL_WRITE_TOOL_NAMES.has(toolName)) {
const filePath = args['file_path'] ?? args['path'] ?? args['target_file'];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] skillsModifiedInSession 检测仅检查 write_file/edit 工具的参数(args['file_path']),无法检测用户通过 shell 工具进行的写入(cp skill.md .qwen/skills/echo > .qwen/skills/x/SKILL.md)。当用户通过 shell 向 skills 目录写入后继续使用,review agent 仍可能被调度,与设计意图("用户本轮已主动操作 skill,跳过自动 review")不一致。建议作为最小化方案,检测到任何 shell 工具调用时将 skillsModifiedInSession 置为 true。

— DeepSeek/deepseek-v4-pro via Qwen Code /review

);

for (const toolCall of geminiTools) {
geminiClient?.recordCompletedToolCall(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] geminiTools 遍历中调用 recordCompletedToolCall 时未过滤已取消(cancelled)的工具调用。已取消的工具从未执行任何实际工作,但会计入 toolCallCount,人工降低技能提炼阈值。且 cancelled 的 write_file/edit 也会设置 skillsModifiedInSession = true,即使用户从未实际提交写入。

Suggested change
geminiClient?.recordCompletedToolCall(
for (const toolCall of geminiTools.filter(tc => tc.status !== 'cancelled')) {
geminiClient?.recordCompletedToolCall(
toolCall.request.name,
toolCall.request.args as Record<string, unknown>,
);
}

— DeepSeek/deepseek-v4-pro via Qwen Code /review

}
}

function getScopedDenyRule(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] getScopedDenyRuleREAD_FILE/LS 返回 undefined(无拒绝规则消息),与 EDIT/WRITE_FILE 的信息丰富的拒绝消息形成对比。当 READ_FILE/LS 因 scope 违规被拒绝时,agent 收到的是毫无信息量的通用 'deny',日志中也看不到拒绝原因,调试困难。建议添加类似 ManagedSkillReview(read_file: only within project root ${projectRoot}) 的拒绝规则。

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/feature-request New feature or enhancement request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants