feat(insight): update static insight generator and command handling

- Refine DataProcessor and StaticInsightGenerator logic
- Update StaticInsightTypes definitions
- Enhance insight command implementation
- Update package dependencies
This commit is contained in:
DragonnZhang 2026-02-05 19:59:20 +08:00
parent 98735fecd7
commit 39d2067d14
6 changed files with 419 additions and 62 deletions

123
package-lock.json generated
View file

@ -416,6 +416,7 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@ -891,6 +892,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
},
@ -914,6 +916,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
}
@ -2353,6 +2356,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=8.0.0"
}
@ -3839,6 +3843,7 @@
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@ -4430,6 +4435,7 @@
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@ -4440,6 +4446,7 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dev": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@ -4652,6 +4659,7 @@
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.35.0",
"@typescript-eslint/types": "8.35.0",
@ -4912,6 +4920,7 @@
"integrity": "sha512-tJxiPrWmzH8a+w9nLKlQMzAKX/7VjFs50MWgcAj7p9XQ7AQ9/35fByFYptgPELyLw+0aixTnC4pUWV+APcZ/kw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@testing-library/dom": "^10.4.0",
"@testing-library/user-event": "^14.6.1",
@ -5062,6 +5071,7 @@
"integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/utils": "3.2.4",
"pathe": "^2.0.3",
@ -5546,6 +5556,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -5960,8 +5971,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/array-includes": {
"version": "3.1.9",
@ -6562,6 +6572,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.25",
"caniuse-lite": "^1.0.30001754",
@ -7345,7 +7356,6 @@
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"safe-buffer": "5.2.1"
},
@ -8501,6 +8511,7 @@
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
@ -9216,7 +9227,6 @@
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@ -9278,7 +9288,6 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.6"
}
@ -9288,7 +9297,6 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"peer": true,
"dependencies": {
"ms": "2.0.0"
}
@ -9298,7 +9306,6 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.8"
}
@ -9506,7 +9513,6 @@
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~2.0.0",
@ -9525,7 +9531,6 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"peer": true,
"dependencies": {
"ms": "2.0.0"
}
@ -9534,15 +9539,13 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/finalhandler/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.8"
}
@ -10650,6 +10653,7 @@
"resolved": "https://registry.npmjs.org/ink/-/ink-6.2.3.tgz",
"integrity": "sha512-fQkfEJjKbLXIcVWEE3MvpYSnwtbbmRsmeNDNz1pIuOFlwE+UF2gsy228J36OXKZGWJWZJKUigphBSqCNMcARtg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@alcalzone/ansi-tokenize": "^0.2.0",
"ansi-escapes": "^7.0.0",
@ -11645,6 +11649,7 @@
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"jiti": "bin/jiti.js"
}
@ -12656,7 +12661,6 @@
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.6"
}
@ -13984,8 +13988,7 @@
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/path-type": {
"version": "3.0.0",
@ -14111,6 +14114,7 @@
"integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"playwright-core": "1.57.0"
},
@ -14148,7 +14152,6 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
@ -14193,6 +14196,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@ -14380,6 +14384,7 @@
"integrity": "sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@ -14723,6 +14728,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -14733,6 +14739,7 @@
"integrity": "sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"shell-quote": "^1.6.1",
"ws": "^7"
@ -14810,6 +14817,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@ -16120,6 +16128,7 @@
"integrity": "sha512-fIQnFtpksRRgHR1CO1onGX3djaog4qsW/c5U8arqYTkUEr2TaWpn05mIJDOBoPJFlOdqFrB4Ttv0PZJxV7avhw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^2.0.1",
@ -17003,6 +17012,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -17202,7 +17212,8 @@
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
"license": "0BSD",
"peer": true
},
"node_modules/tsx": {
"version": "4.20.3",
@ -17210,6 +17221,7 @@
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "~0.25.0",
"get-tsconfig": "^4.7.5"
@ -17404,6 +17416,7 @@
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -17751,7 +17764,6 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.4.0"
}
@ -17807,6 +17819,7 @@
"integrity": "sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.6",
@ -17920,6 +17933,7 @@
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -17933,6 +17947,7 @@
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4",
@ -18469,6 +18484,7 @@
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
"dev": true,
"license": "ISC",
"peer": true,
"bin": {
"yaml": "bin.mjs"
},
@ -18649,6 +18665,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
@ -18676,6 +18693,7 @@
"ink-spinner": "^5.0.0",
"lowlight": "^3.3.0",
"open": "^10.1.2",
"p-limit": "^7.3.0",
"prompts": "^2.4.2",
"qrcode-terminal": "^0.12.0",
"react": "^19.1.0",
@ -18748,6 +18766,7 @@
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.1.tgz",
"integrity": "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@hono/node-server": "^1.19.7",
"ajv": "^8.17.1",
@ -19142,6 +19161,21 @@
"url": "https://opencollective.com/node-fetch"
}
},
"packages/cli/node_modules/p-limit": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz",
"integrity": "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==",
"license": "MIT",
"dependencies": {
"yocto-queue": "^1.2.1"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"packages/cli/node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
@ -19272,6 +19306,18 @@
"node": ">=18.17"
}
},
"packages/cli/node_modules/yocto-queue": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
"integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
"license": "MIT",
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"packages/core": {
"name": "@qwen-code/qwen-code-core",
"version": "0.10.0",
@ -19377,6 +19423,7 @@
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.1.tgz",
"integrity": "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@hono/node-server": "^1.19.7",
"ajv": "^8.17.1",
@ -19771,6 +19818,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -20533,6 +20581,7 @@
"integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "7.18.0",
"@typescript-eslint/types": "7.18.0",
@ -20702,39 +20751,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"packages/sdk-typescript/node_modules/@vitest/browser": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-1.6.1.tgz",
"integrity": "sha512-9ZYW6KQ30hJ+rIfJoGH4wAub/KAb4YrFzX0kVLASvTm7nJWVC5EAv5SlzlXVl3h3DaUq5aqHlZl77nmOPnALUQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@vitest/utils": "1.6.1",
"magic-string": "^0.30.5",
"sirv": "^2.0.4"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"playwright": "*",
"vitest": "1.6.1",
"webdriverio": "*"
},
"peerDependenciesMeta": {
"playwright": {
"optional": true
},
"safaridriver": {
"optional": true
},
"webdriverio": {
"optional": true
}
}
},
"packages/sdk-typescript/node_modules/@vitest/coverage-v8": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.1.tgz",
@ -21046,6 +21062,7 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@ -21549,7 +21566,6 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@polka/url": "^1.0.0-next.24",
"mrmime": "^2.0.0",
@ -22189,6 +22205,7 @@
"integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/expect": "1.6.1",
"@vitest/runner": "1.6.1",
@ -23870,6 +23887,7 @@
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -23884,6 +23902,7 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",

View file

@ -56,6 +56,7 @@
"ink-spinner": "^5.0.0",
"lowlight": "^3.3.0",
"open": "^10.1.2",
"p-limit": "^7.3.0",
"prompts": "^2.4.2",
"qrcode-terminal": "^0.12.0",
"react": "^19.1.0",
@ -81,12 +82,12 @@
"@types/diff": "^7.0.2",
"@types/dotenv": "^6.1.1",
"@types/node": "^20.11.24",
"@types/prompts": "^2.4.9",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@types/semver": "^7.7.0",
"@types/shell-quote": "^1.7.5",
"@types/yargs": "^17.0.32",
"@types/prompts": "^2.4.9",
"archiver": "^7.0.1",
"ink-testing-library": "^4.0.0",
"jsdom": "^26.1.0",

View file

@ -7,21 +7,201 @@
import fs from 'fs/promises';
import path from 'path';
import { read as readJsonlFile } from '@qwen-code/qwen-code-core';
import pLimit from 'p-limit';
import type { Config, ChatRecord } from '@qwen-code/qwen-code-core';
import type {
InsightData,
HeatMapData,
TokenUsageData,
AchievementData,
StreakData,
SessionFacets,
} from '../types/StaticInsightTypes.js';
import type { ChatRecord } from '@qwen-code/qwen-code-core';
// Prompt content from prompt.txt
const ANALYSIS_PROMPT = `Analyze this Qwen Code session and extract structured facets.
CRITICAL GUIDELINES:
1. **goal_categories**: Count ONLY what the USER explicitly asked for.
- DO NOT count Qwen's autonomous codebase exploration
- DO NOT count work Qwen decided to do on its own
- ONLY count when user says "can you...", "please...", "I need...", "let's..."
2. **user_satisfaction_counts**: Base ONLY on explicit user signals.
- "Yay!", "great!", "perfect!" happy
- "thanks", "looks good", "that works" satisfied
- "ok, now let's..." (continuing without complaint) likely_satisfied
- "that's not right", "try again" dissatisfied
- "this is broken", "I give up" frustrated
3. **friction_counts**: Be specific about what went wrong.
- misunderstood_request: Qwen interpreted incorrectly
- wrong_approach: Right goal, wrong solution method
- buggy_code: Code didn't work correctly
- user_rejected_action: User said no/stop to a tool call
- excessive_changes: Over-engineered or changed too much
4. If very short or just warmup, use warmup_minimal for goal_category`;
const INSIGHT_SCHEMA = {
type: 'object',
properties: {
underlying_goal: {
type: 'string',
description: 'What the user fundamentally wanted to achieve',
},
goal_categories: {
type: 'object',
additionalProperties: { type: 'number' },
},
outcome: {
type: 'string',
enum: [
'fully_achieved',
'mostly_achieved',
'partially_achieved',
'not_achieved',
'unclear_from_transcript',
],
},
user_satisfaction_counts: {
type: 'object',
additionalProperties: { type: 'number' },
},
Qwen_helpfulness: {
type: 'string',
enum: [
'unhelpful',
'slightly_helpful',
'moderately_helpful',
'very_helpful',
'essential',
],
},
session_type: {
type: 'string',
enum: [
'single_task',
'multi_task',
'iterative_refinement',
'exploration',
'quick_question',
],
},
friction_counts: {
type: 'object',
additionalProperties: { type: 'number' },
},
friction_detail: {
type: 'string',
description: 'One sentence describing friction or empty',
},
primary_success: {
type: 'string',
enum: [
'none',
'fast_accurate_search',
'correct_code_edits',
'good_explanations',
'proactive_help',
'multi_file_changes',
'good_debugging',
],
},
brief_summary: {
type: 'string',
description: 'One sentence: what user wanted and whether they got it',
},
},
required: [
'underlying_goal',
'goal_categories',
'outcome',
'user_satisfaction_counts',
'Qwen_helpfulness',
'session_type',
'friction_counts',
'friction_detail',
'primary_success',
'brief_summary',
],
};
export class DataProcessor {
constructor(private config: Config) {}
// Helper function to format date as YYYY-MM-DD
private formatDate(date: Date): string {
return date.toISOString().split('T')[0];
}
// Format chat records for LLM analysis
private formatRecordsForAnalysis(records: ChatRecord[]): string {
let output = '';
const sessionStart =
records.length > 0 ? new Date(records[0].timestamp) : new Date();
output += `Session: ${records[0]?.sessionId || 'unknown'}\n`;
output += `Date: ${sessionStart.toISOString()}\n`;
output += `Duration: ${records.length} turns\n\n`;
for (const record of records) {
if (record.type === 'user') {
const text =
record.message?.parts
?.map((p) => ('text' in p ? p.text : ''))
.join('') || '';
output += `[User]: ${text}\n`;
} else if (record.type === 'assistant') {
if (record.message?.parts) {
for (const part of record.message.parts) {
if ('text' in part && part.text) {
output += `[Assistant]: ${part.text}\n`;
} else if ('functionCall' in part) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const call = (part as any).functionCall;
if (call) {
output += `[Tool: ${call.name}]\n`;
}
}
}
}
}
}
return output;
}
// Analyze a single session using LLM
private async analyzeSession(
records: ChatRecord[],
): Promise<SessionFacets | null> {
if (records.length === 0) return null;
const sessionText = this.formatRecordsForAnalysis(records);
const prompt = `${ANALYSIS_PROMPT}\n\nSESSION:\n${sessionText}`;
try {
const result = await this.config.getBaseLlmClient().generateJson({
// Use the configured model
model: this.config.getModel(),
contents: [{ role: 'user', parts: [{ text: prompt }] }],
schema: INSIGHT_SCHEMA,
abortSignal: AbortSignal.timeout(60000), // 1 minute timeout per session
});
return {
...(result as unknown as SessionFacets),
session_id: records[0].sessionId,
};
} catch (error) {
console.error(
`Failed to analyze session ${records[0]?.sessionId}:`,
error,
);
return null;
}
}
// Calculate streaks from activity dates
private calculateStreaks(dates: string[]): StreakData {
if (dates.length === 0) {
@ -183,7 +363,10 @@ export class DataProcessor {
}
// Process chat files from all projects in the base directory and generate insights
async generateInsights(baseDir: string): Promise<InsightData> {
async generateInsights(
baseDir: string,
facetsOutputDir?: string,
): Promise<InsightData> {
// Initialize data structures
const heatmap: HeatMapData = {};
const tokenUsage: TokenUsageData = {};
@ -191,6 +374,9 @@ export class DataProcessor {
const sessionStartTimes: { [sessionId: string]: Date } = {};
const sessionEndTimes: { [sessionId: string]: Date } = {};
// Store all valid chat file paths for LLM analysis
const allChatFiles: Array<{ path: string; mtime: number }> = [];
try {
// Get all project directories in the base directory
const projectDirs = await fs.readdir(baseDir);
@ -222,6 +408,15 @@ export class DataProcessor {
// Process each chat file in this project
for (const file of chatFiles) {
const filePath = path.join(chatsDir, file);
// Get file stats for sorting by recency
try {
const fileStats = await fs.stat(filePath);
allChatFiles.push({ path: filePath, mtime: fileStats.mtimeMs });
} catch (e) {
console.error(`Failed to stat file ${filePath}:`, e);
}
const records = await readJsonlFile<ChatRecord>(filePath);
// Process each record
@ -269,6 +464,84 @@ export class DataProcessor {
}
}
// Sort files by recency (descending) and take top 50
const recentFiles = allChatFiles
.sort((a, b) => b.mtime - a.mtime)
.slice(0, 50);
console.log(`Analyzing ${recentFiles.length} recent sessions with LLM...`);
// Create a limit function with concurrency of 4 to avoid 429 errors
const limit = pLimit(4);
// Analyze sessions concurrently with limit
const analysisPromises = recentFiles.map((fileInfo) =>
limit(async () => {
try {
const records = await readJsonlFile<ChatRecord>(fileInfo.path);
// Check if we already have this session analyzed
if (records.length > 0 && facetsOutputDir) {
const sessionId = records[0].sessionId;
if (sessionId) {
const existingFacetPath = path.join(
facetsOutputDir,
`${sessionId}.json`,
);
try {
// Check if file exists and is readable
const existingData = await fs.readFile(
existingFacetPath,
'utf-8',
);
const existingFacet = JSON.parse(existingData);
return existingFacet;
} catch (readError) {
// File doesn't exist or is invalid, proceed to analyze
if ((readError as NodeJS.ErrnoException).code !== 'ENOENT') {
console.warn(
`Failed to read existing facet for ${sessionId}, regenerating:`,
readError,
);
}
}
}
}
const facet = await this.analyzeSession(records);
if (facet && facetsOutputDir) {
try {
const facetPath = path.join(
facetsOutputDir,
`${facet.session_id}.json`,
);
await fs.writeFile(
facetPath,
JSON.stringify(facet, null, 2),
'utf-8',
);
} catch (writeError) {
console.error(
`Failed to write facet file for session ${facet.session_id}:`,
writeError,
);
}
}
return facet;
} catch (e) {
console.error(`Error analyzing session file ${fileInfo.path}:`, e);
return null;
}
}),
);
const sessionFacetsWithNulls = await Promise.all(analysisPromises);
const facets = sessionFacetsWithNulls.filter(
(f): f is SessionFacets => f !== null,
);
// Calculate streak data
const streakData = this.calculateStreaks(Object.keys(heatmap));
@ -319,6 +592,7 @@ export class DataProcessor {
activeHours,
latestActiveTime,
achievements,
facets,
};
}
}

View file

@ -11,12 +11,14 @@ import { DataProcessor } from './DataProcessor.js';
import { TemplateRenderer } from './TemplateRenderer.js';
import type { InsightData } from '../types/StaticInsightTypes.js';
import type { Config } from '@qwen-code/qwen-code-core';
export class StaticInsightGenerator {
private dataProcessor: DataProcessor;
private templateRenderer: TemplateRenderer;
constructor() {
this.dataProcessor = new DataProcessor();
constructor(config: Config) {
this.dataProcessor = new DataProcessor(config);
this.templateRenderer = new TemplateRenderer();
}
@ -30,23 +32,42 @@ export class StaticInsightGenerator {
// Generate the static insight HTML file
async generateStaticInsight(baseDir: string): Promise<string> {
try {
// Ensure output directory exists
const outputDir = await this.ensureOutputDirectory();
const facetsDir = path.join(outputDir, 'facets');
await fs.mkdir(facetsDir, { recursive: true });
// Process data
console.log('Processing insight data...');
const insights: InsightData =
await this.dataProcessor.generateInsights(baseDir);
const insights: InsightData = await this.dataProcessor.generateInsights(
baseDir,
facetsDir,
);
// Render HTML
console.log('Rendering HTML template...');
const html = await this.templateRenderer.renderInsightHTML(insights);
// Ensure output directory exists
const outputDir = await this.ensureOutputDirectory();
const outputPath = path.join(outputDir, 'insight.html');
// Write the HTML file
console.log(`Writing HTML file to: ${outputPath}`);
await fs.writeFile(outputPath, html, 'utf-8');
// Write the JSON data file
const jsonPath = path.join(outputDir, 'insight.json');
console.log(`Writing JSON data to: ${jsonPath}`);
// Exclude facets from the main JSON file as they are stored individually
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { facets, ...insightsWithoutFacets } = insights;
await fs.writeFile(
jsonPath,
JSON.stringify(insightsWithoutFacets, null, 2),
'utf-8',
);
console.log('Static insight generation completed successfully');
return outputPath;
} catch (error) {

View file

@ -34,6 +34,7 @@ export interface InsightData {
activeHours: { [hour: number]: number };
latestActiveTime: string | null;
achievements: AchievementData[];
facets?: SessionFacets[];
}
export interface StreakData {
@ -42,6 +43,42 @@ export interface StreakData {
dates: string[];
}
export interface SessionFacets {
session_id: string;
underlying_goal: string;
goal_categories: Record<string, number>;
outcome:
| 'fully_achieved'
| 'mostly_achieved'
| 'partially_achieved'
| 'not_achieved'
| 'unclear_from_transcript';
user_satisfaction_counts: Record<string, number>;
Qwen_helpfulness:
| 'unhelpful'
| 'slightly_helpful'
| 'moderately_helpful'
| 'very_helpful'
| 'essential';
session_type:
| 'single_task'
| 'multi_task'
| 'iterative_refinement'
| 'exploration'
| 'quick_question';
friction_counts: Record<string, number>;
friction_detail: string;
primary_success:
| 'none'
| 'fast_accurate_search'
| 'correct_code_edits'
| 'good_explanations'
| 'proactive_help'
| 'multi_file_changes'
| 'good_debugging';
brief_summary: string;
}
export interface StaticInsightTemplateData {
styles: string;
content: string;

View file

@ -60,7 +60,12 @@ export const insightCommand: SlashCommand = {
context.ui.setDebugMessage(t('Generating insights...'));
const projectsDir = join(os.homedir(), '.qwen', 'projects');
const insightGenerator = new StaticInsightGenerator();
if (!context.services.config) {
throw new Error('Config service is not available');
}
const insightGenerator = new StaticInsightGenerator(
context.services.config,
);
context.ui.addItem(
{