diff --git a/package-lock.json b/package-lock.json index ff5a902d7..11ee4087d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/packages/cli/package.json b/packages/cli/package.json index 729f04a33..af91de45e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -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", diff --git a/packages/cli/src/services/insight/generators/DataProcessor.ts b/packages/cli/src/services/insight/generators/DataProcessor.ts index 768d92fd1..c5928ef54 100644 --- a/packages/cli/src/services/insight/generators/DataProcessor.ts +++ b/packages/cli/src/services/insight/generators/DataProcessor.ts @@ -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 { + 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 { + async generateInsights( + baseDir: string, + facetsOutputDir?: string, + ): Promise { // 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(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(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, }; } } diff --git a/packages/cli/src/services/insight/generators/StaticInsightGenerator.ts b/packages/cli/src/services/insight/generators/StaticInsightGenerator.ts index 6e8edfb52..f89a8032e 100644 --- a/packages/cli/src/services/insight/generators/StaticInsightGenerator.ts +++ b/packages/cli/src/services/insight/generators/StaticInsightGenerator.ts @@ -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 { 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) { diff --git a/packages/cli/src/services/insight/types/StaticInsightTypes.ts b/packages/cli/src/services/insight/types/StaticInsightTypes.ts index c79589f17..d1d132e66 100644 --- a/packages/cli/src/services/insight/types/StaticInsightTypes.ts +++ b/packages/cli/src/services/insight/types/StaticInsightTypes.ts @@ -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; + outcome: + | 'fully_achieved' + | 'mostly_achieved' + | 'partially_achieved' + | 'not_achieved' + | 'unclear_from_transcript'; + user_satisfaction_counts: Record; + Qwen_helpfulness: + | 'unhelpful' + | 'slightly_helpful' + | 'moderately_helpful' + | 'very_helpful' + | 'essential'; + session_type: + | 'single_task' + | 'multi_task' + | 'iterative_refinement' + | 'exploration' + | 'quick_question'; + friction_counts: Record; + 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; diff --git a/packages/cli/src/ui/commands/insightCommand.ts b/packages/cli/src/ui/commands/insightCommand.ts index 45a96af59..04a0497e1 100644 --- a/packages/cli/src/ui/commands/insightCommand.ts +++ b/packages/cli/src/ui/commands/insightCommand.ts @@ -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( {