From d9d5458de1f1e7cc8fa0fb1e293857c0c60b1763 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 00:59:32 +0000 Subject: [PATCH 01/34] chore(deps): update dev dependencies (non-major) --- package-lock.json | 351 ++++++++++++++++++++++++++++++---------------- 1 file changed, 230 insertions(+), 121 deletions(-) diff --git a/package-lock.json b/package-lock.json index e60618b6..0a358f03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -427,9 +427,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.1.tgz", - "integrity": "sha512-IXWLCxKmae+rI7LOHS1B3EbVisQ6GRAWbhN9msa6KjNCyFWrvKZWR4oUdinaNssrV852OrSHuSPa95h1GPJc7Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.2.tgz", + "integrity": "sha512-VQ3RCqr7JmDIX+w6stWYl+g/3bYofN3q2wDBHUKKc/c7i5QWrFKFBZYCYPWTE6agsUPMIZZe6/CMmVUfUAhkKA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -443,20 +443,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.1", - "@biomejs/cli-darwin-x64": "2.5.1", - "@biomejs/cli-linux-arm64": "2.5.1", - "@biomejs/cli-linux-arm64-musl": "2.5.1", - "@biomejs/cli-linux-x64": "2.5.1", - "@biomejs/cli-linux-x64-musl": "2.5.1", - "@biomejs/cli-win32-arm64": "2.5.1", - "@biomejs/cli-win32-x64": "2.5.1" + "@biomejs/cli-darwin-arm64": "2.5.2", + "@biomejs/cli-darwin-x64": "2.5.2", + "@biomejs/cli-linux-arm64": "2.5.2", + "@biomejs/cli-linux-arm64-musl": "2.5.2", + "@biomejs/cli-linux-x64": "2.5.2", + "@biomejs/cli-linux-x64-musl": "2.5.2", + "@biomejs/cli-win32-arm64": "2.5.2", + "@biomejs/cli-win32-x64": "2.5.2" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.1.tgz", - "integrity": "sha512-npqDzvqv7vFaWRiNN1Te71siRgPaqS9MpqgYCdP/CrUbkJ7ApezaeaKjueKHRN/JH/6lRjJQAHi8acQDCAz22w==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.2.tgz", + "integrity": "sha512-e7P3P7EkwFc/KiX2AHw4YDLIBOMfG9CPCAwy52k5Bp0dfhkozx9hf6wCmIr2QeXy2XeccJ3V/Sg+hDmzYEqxSg==", "cpu": [ "arm64" ], @@ -471,9 +471,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.1.tgz", - "integrity": "sha512-RgwTqPAM8g2tn1j+b5oRjF/DbSBX8a4gwojtuG9XuhfK7GgomvZ9+T+tqjXiVbjLEeGJOoL6VEk8mvRTVeSybw==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.2.tgz", + "integrity": "sha512-ymzMvjC1Jg0b9K0D26ZdARqFQXs7MocfLC5FOCGfkC0Ss+ACUJkX5364ZM5nT4NLZanHRZNVrZEy+Ibwcvux/g==", "cpu": [ "x64" ], @@ -488,9 +488,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.1.tgz", - "integrity": "sha512-yhV35CzZh38VyMvTEXi3JTjxZBs++oCKK9KG8vB6VI5+uvQvZNR3BFWEKKzuOmx9DJJj7sQpZ4LQJcmbGTs3+Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.2.tgz", + "integrity": "sha512-t7sseOmqND57uUWTwlawU6BYj+J06T/9EkydzBhkrgw/FK3QVhjU2wsJR0frljrKZ0/I8A/rYw7284QgqjQfIQ==", "cpu": [ "arm64" ], @@ -508,9 +508,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.1.tgz", - "integrity": "sha512-WMcvMLgByyTqVxGlq918NBBYliq9FRR9GAQVETHb+VjGVqXCZFfHlZHC1FX4ibuYY/Hg6TJE3rHU0xVrdJXNRw==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.2.tgz", + "integrity": "sha512-w+ANG0ZvTu9IeEg9QnstoOnk6L0fpwJifW6aHR18+cb5Z39bkANItYjAfMrnvce5tmMK+IQ6nPX7/kQFdam5iw==", "cpu": [ "arm64" ], @@ -528,9 +528,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.1.tgz", - "integrity": "sha512-J/7uHSX7NfoYDI7HijAkd8lnQIOrRb2W7j3X+tw4R+N5ExvXGsyXFiGdQcfcxfOmNQmZVSQOCDk757fwpzqQcg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.2.tgz", + "integrity": "sha512-M/lOZrewzTCRDINbjhQ1gYYru37KlD3kJBQwwKCG0ckz5E9IZwIoJ3X0wBwRXA+yBDIwWUuPBHS67HzJY4dTfA==", "cpu": [ "x64" ], @@ -548,9 +548,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.1.tgz", - "integrity": "sha512-ANTowtlLmPYm5yeMckWY8Xzb9Ix+JJP3tgHR/n6xRj1VWyIzzWtfRfih9hv9VmClwadpBvZduISZIbBsIlYG3A==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.2.tgz", + "integrity": "sha512-VArNLAzND063tF+XY0yPyM+DyahpzOMzOAvb7qs259nhjJWRjvjZdssuA+Rfl+l07+NOesKZ0Xu2yFrXyBMtzw==", "cpu": [ "x64" ], @@ -568,9 +568,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.1.tgz", - "integrity": "sha512-zgXnKNgWPC4iPF7Y1lR3STUeCUuZRpD6IiOrC7TZTlh0Lx6FiVUT05myuMQHQ9D+1cc7uyMldi4forE6lp0ivQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.2.tgz", + "integrity": "sha512-kbjFFKyZlzYnAuw7sRy5qDoFG6zrP40UK08oPQsWK0ct3NMnGSt+Bs1iviEEyEIP57N5MrykGXdO/wRiaR4lww==", "cpu": [ "arm64" ], @@ -585,9 +585,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.1.tgz", - "integrity": "sha512-6uxpR9hvaglANkZemeSiN/FhYgkGasrEGn267eXIWvjrjJ2LhDlk251IhjVJq6MXzkV2/bcXwLwSroLyPtqRZg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.2.tgz", + "integrity": "sha512-4InchVpdVmdkkkgjQqKpgvyu+VPnoF/7RPSw5YATgEVpt2j72wcCAeV5TwaE9ZGJUZWZn7v2CwSAj6CrMJEx8A==", "cpu": [ "x64" ], @@ -613,18 +613,18 @@ } }, "node_modules/@commitlint/cli": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.1.0.tgz", - "integrity": "sha512-CVwY6TxGv5naEaWxBdgNHko1xgL95Mb4WcIqp9iik33H0ctVqRv6YtekCntayhEP0T/apuiGvHu5HcCwFuVxEA==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.0.tgz", + "integrity": "sha512-4GLVIhUaT3c3GBlQ0GB80/5H3xXdn/Tgw4lrsuoOQVDu2wl4Xw0GuzSar8xZKSMv4H3xaKaQXmvH91GmdyYBZA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-conventional": "^21.1.0", - "@commitlint/format": "^21.1.0", - "@commitlint/lint": "^21.1.0", - "@commitlint/load": "^21.1.0", - "@commitlint/read": "^21.1.0", - "@commitlint/types": "^21.1.0", + "@commitlint/config-conventional": "^21.2.0", + "@commitlint/format": "^21.2.0", + "@commitlint/lint": "^21.2.0", + "@commitlint/load": "^21.2.0", + "@commitlint/read": "^21.2.0", + "@commitlint/types": "^21.2.0", "tinyexec": "^1.0.0", "yargs": "^18.0.0" }, @@ -636,27 +636,27 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.1.0.tgz", - "integrity": "sha512-BIFl8xM+3SLy3jrblUC3wmQLCVbLty+++6o859BDCmybVrQdXmIWO+dlkGIbv/M2bBoC55wGuh0zGiw3TPjL1g==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.0.tgz", + "integrity": "sha512-Qf8WRDVcyVd14if6VTWenebxFbKnVnbzPUJjlzjkyJGeHK2xCGd63Dr1XZzj0plXKQb9P0BfOxoc1HVeCo2BWQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.1.0", - "conventional-changelog-conventionalcommits": "^9.2.0" + "@commitlint/types": "^21.2.0", + "conventional-changelog-conventionalcommits": "^10.0.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/config-validator": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.1.0.tgz", - "integrity": "sha512-gHczt1xqQSwfNqBmOI3HjejtTljkiBEUneExMmTBLD0WwTC78lAqDvNMyydbySt3DhpH0F9oX7Vvuks6s5XPFw==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.2.0.tgz", + "integrity": "sha512-t7AzNHAKeIdo/3NRGwzpufKHsKkPHmFs/56N2Fnsh0/r0rGtnQzTxk6vnFgjaGr4hdSQKNB50/KAhR9Yk4LJKA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.1.0", + "@commitlint/types": "^21.2.0", "ajv": "^8.11.0" }, "engines": { @@ -664,13 +664,13 @@ } }, "node_modules/@commitlint/ensure": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.1.0.tgz", - "integrity": "sha512-/S8Mo3Q1NtQUYDQjDmyQVPxfIwtnxq+guzMOkuGk8OSdwlzanm1WB9wDPIuuzlbMDDnBNbiAuBEUCcCNlfjrTQ==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.2.0.tgz", + "integrity": "sha512-76IF9vDNS13lAzEEik9eKwzt8f9hYhWiwVXZ2AnyLCz5/f511FsEQ3pw1X3/zSQpdRLQU7i5qDMVKyXi1GWjSg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.1.0", + "@commitlint/types": "^21.2.0", "es-toolkit": "^1.46.0" }, "engines": { @@ -688,13 +688,13 @@ } }, "node_modules/@commitlint/format": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.1.0.tgz", - "integrity": "sha512-ySymqKYBfjNrQ5N4W/l1iF2ISW1W7Eu/Oi/wRxlri31N0yjNyzUyUzQwyuZLDzTXIlMs4IZ7hIOfAZx8lO18gA==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.0.tgz", + "integrity": "sha512-c4q64xaav2U83t7k7RyzJerBZurPer7FxUOY0RL5L/6CZijZ7K+s6HIBGIghj0ey1P2+seRX0J9XQYtDued6tg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.1.0", + "@commitlint/types": "^21.2.0", "picocolors": "^1.1.1" }, "engines": { @@ -702,13 +702,13 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.1.0.tgz", - "integrity": "sha512-RoRh1/YI+fYH+aid5lMQ2UD0vZ3p3Vf1KeUWT1ir3H/p/7T/6SFv1OiXLgLwUT8dP72EVWeEIyOfkiSWLZYVvw==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.0.tgz", + "integrity": "sha512-4/eB0vBN7L88O/oC4ajAEqi7j2ZfNgxl/+11RfAV9YosejZgDXhY2C9VcHnHJhOzPLoSy5P3Mg/46kqeyJfXKw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.1.0", + "@commitlint/types": "^21.2.0", "semver": "^7.6.0" }, "engines": { @@ -716,32 +716,32 @@ } }, "node_modules/@commitlint/lint": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.1.0.tgz", - "integrity": "sha512-0DbfVVUjAWBfixW6v7CXXWVxMcj6Ukf/oB7O8NAbouP3jxmqUaC4eVQphxl3B3M0ii3cCQiR3sRAYxICwU2gAA==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.0.tgz", + "integrity": "sha512-ceO5dp9pLjEZ6y6qbq/uXWXDPykqqlTsyzoQ0NzecpisSJhK3kTy9qzQoPeJuWG/IMNdV1lO0RgmzqoAlSi1uw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/is-ignored": "^21.1.0", - "@commitlint/parse": "^21.1.0", - "@commitlint/rules": "^21.1.0", - "@commitlint/types": "^21.1.0" + "@commitlint/is-ignored": "^21.2.0", + "@commitlint/parse": "^21.2.0", + "@commitlint/rules": "^21.2.0", + "@commitlint/types": "^21.2.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/load": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.1.0.tgz", - "integrity": "sha512-juiClVEcoreNB0TNVkseO2EmNcpEs/Yhnmgbnm/hQAKBFRynKwIaoNIljXkx/3yvZcMO0EE8I2XOEI7d5KZG8Q==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.0.tgz", + "integrity": "sha512-RjlzWQqruRwIenJEfZtq7kG97co97nKoHpflE5YnF61tDLXxHPrdWImgzw6VL6MlFyaOcVlk74eBV8ZQmc3oIA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^21.1.0", + "@commitlint/config-validator": "^21.2.0", "@commitlint/execute-rule": "^21.0.1", - "@commitlint/resolve-extends": "^21.1.0", - "@commitlint/types": "^21.1.0", + "@commitlint/resolve-extends": "^21.2.0", + "@commitlint/types": "^21.2.0", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", "es-toolkit": "^1.46.0", @@ -753,9 +753,9 @@ } }, "node_modules/@commitlint/message": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.0.2.tgz", - "integrity": "sha512-5n4aqHGD/FNnom/D5L8i7cYtV+xjuXcBL832C3w9VglEsZzIsoHpJsvxzJ7cgiOsOdc/2jU4t5+7qMHh7GBX3g==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.2.0.tgz", + "integrity": "sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g==", "dev": true, "license": "MIT", "engines": { @@ -763,29 +763,85 @@ } }, "node_modules/@commitlint/parse": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.1.0.tgz", - "integrity": "sha512-HdAqbbjQS8eEtbR74Ysg2VNmbvAfeWLVYMkip/lHibNrtjRsC/97XAYN3/H5P0pEJtDfyTb3iLs8x6y0eu4OYA==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.0.tgz", + "integrity": "sha512-QHWxG4d0PLTF634/AdyZ0MQS+CLn5YOuJlCFhMMlSGKFxzYGUetkHBj18xgBD+6fVzUrA2lrCdi/vlS2f/oYXg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^21.1.0", - "conventional-changelog-angular": "^8.2.0", - "conventional-commits-parser": "^6.3.0" + "@commitlint/types": "^21.2.0", + "conventional-changelog-angular": "^9.0.0", + "conventional-commits-parser": "^7.0.0" }, "engines": { "node": ">=22.12.0" } }, + "node_modules/@commitlint/parse/node_modules/@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@commitlint/parse/node_modules/conventional-changelog-angular": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.2.0.tgz", + "integrity": "sha512-2EihZvM1KmbBGwuUow8lNXLe+ECRFsyOcV8X0197/WI7Rvvgfi55Oicfw0wxJTXGGIVXSfj+xBoNj+cxLlIiVw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@conventional-changelog/template": "^1.2.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@commitlint/parse/node_modules/conventional-commits-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.0.0.tgz", + "integrity": "sha512-dZe/p+FgGQLNJFqaCgNdl8j6a7gI8xuaN30Wy5g7nvyK3jqOpNUEUZ3pGJ5D68h89uRh038FtkeOk/bnGmYkmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^2.0.0", + "meow": "^14.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@commitlint/parse/node_modules/meow": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz", + "integrity": "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@commitlint/read": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.1.0.tgz", - "integrity": "sha512-ID7m79aw8d0dMlxuXHD2QGxEX3Fhl/mUPA80WwEW5VgeOpUHNahhwWJefDdoBDVZcDfbHuf429NrcK0gxQsQjA==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.0.tgz", + "integrity": "sha512-ELx8Ovh/JoAw5lpvDgxc6Y0We9skf2IPI2RFN+gnYgDGjRdMSF8zeodxhZmcclLWzfUIF7hXLOa8gOlllYcvBA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/top-level": "^21.0.2", - "@commitlint/types": "^21.1.0", + "@commitlint/top-level": "^21.2.0", + "@commitlint/types": "^21.2.0", "git-raw-commits": "^5.0.0", "tinyexec": "^1.0.0" }, @@ -794,14 +850,14 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.1.0.tgz", - "integrity": "sha512-SANYkxJDfMl3TvnyALWHEaiF5nc6FFaOnh7VvfxjT4X2vD4i2gVHhmfMm1fsrBwDRX98/XyM1XDo5sAd/KXcyQ==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.0.tgz", + "integrity": "sha512-4O/1j51+79Wth9s/MGxt/5gs0XYLDgNlYpltQfhAvLE0itusLKs9zruxbiNg1oOkmkb9L9L4USYGjEj7n87NxA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^21.1.0", - "@commitlint/types": "^21.1.0", + "@commitlint/config-validator": "^21.2.0", + "@commitlint/types": "^21.2.0", "es-toolkit": "^1.46.0", "global-directory": "^5.0.0", "resolve-from": "^5.0.0" @@ -811,16 +867,16 @@ } }, "node_modules/@commitlint/rules": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.1.0.tgz", - "integrity": "sha512-fOPEYSmKn1ZJptjLmCEjJfYqz0PUYr8ng6VY2ZW26sB7KtENR90CmAXHEmScBbOIZip+d/+OwqK12DFBuHTqsQ==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.0.tgz", + "integrity": "sha512-C2yXMNpiB8ETZKfx5JD8+ExgF8vTU1VQMKPSUUYwqKpw9oJWQBrlXBpdU038mj2WPjof7o9UzFpmTyBeGMZwZg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/ensure": "^21.1.0", - "@commitlint/message": "^21.0.2", + "@commitlint/ensure": "^21.2.0", + "@commitlint/message": "^21.2.0", "@commitlint/to-lines": "^21.0.1", - "@commitlint/types": "^21.1.0" + "@commitlint/types": "^21.2.0" }, "engines": { "node": ">=22.12.0" @@ -837,9 +893,9 @@ } }, "node_modules/@commitlint/top-level": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.0.2.tgz", - "integrity": "sha512-s9KKM+e+mXgFeIh4n7KmOGAVT3mkJ3Fp1bBYHIK5pjeUwlEMzp/tZfb5u0Poa680AsQTXMEMRxZi1vQ9m2X5ug==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.2.0.tgz", + "integrity": "sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -850,19 +906,62 @@ } }, "node_modules/@commitlint/types": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.1.0.tgz", - "integrity": "sha512-YodnnnH1Cp+08nP8HGNJAIuB6L3/vdCTHVRTfF8Ik/wRCLOTsU9zwv3yO1cSPQRDa9CLYtE+UJ2K67r7CwMSFw==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.0.tgz", + "integrity": "sha512-7zVFCDB2reMvJH5dmbKnOQPjZEvjdJTH8jc0U/PIPU1r3/+vf5pD1HlfitV2MWsWXrvu7u39iY1lyLUPOaN0Gw==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-parser": "^6.3.0", + "conventional-commits-parser": "^7.0.0", "picocolors": "^1.1.1" }, "engines": { "node": ">=22.12.0" } }, + "node_modules/@commitlint/types/node_modules/@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@commitlint/types/node_modules/conventional-commits-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.0.0.tgz", + "integrity": "sha512-dZe/p+FgGQLNJFqaCgNdl8j6a7gI8xuaN30Wy5g7nvyK3jqOpNUEUZ3pGJ5D68h89uRh038FtkeOk/bnGmYkmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^2.0.0", + "meow": "^14.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@commitlint/types/node_modules/meow": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz", + "integrity": "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@conventional-changelog/git-client": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.7.0.tgz", @@ -890,6 +989,16 @@ } } }, + "node_modules/@conventional-changelog/template": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.2.0.tgz", + "integrity": "sha512-12qHxvlKjHmP0PQ+17EREgC7lWyLwbph1RKcZQZ7k7ZWGmrxfxC9gadHGfvzr0g0u8BhiBGg3tks93txodlyRQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, "node_modules/@emnapi/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", @@ -4108,9 +4217,9 @@ } }, "node_modules/@semantic-release/github": { - "version": "12.0.8", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-12.0.8.tgz", - "integrity": "sha512-tej5AAgK5X9wHRoDmYhecMXEHEkFeGOY1XsEblKxu8pIQwahzf1STYyr7iPU6Lpbg6C5I3N2w/ocXrBo+L7jhw==", + "version": "12.0.9", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-12.0.9.tgz", + "integrity": "sha512-ODIqb0V3QqndipryEEiaBxUQCFjvv7Oese5Dt4omMGa60YRNEW0Sx3K+zri0uac2Y6S9nOlMehciWIzvvRCTGQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5588,16 +5697,16 @@ } }, "node_modules/conventional-changelog-conventionalcommits": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.1.tgz", - "integrity": "sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.2.0.tgz", + "integrity": "sha512-UtlM9GqolY7OmlQh5L/UEVoKsTUpTgUVy1PU8JN5gl5Ydaejb7WRklGliG1SKPxxj7hzA173eG3Kt5fYWE2pmg==", "dev": true, "license": "ISC", "dependencies": { - "compare-func": "^2.0.0" + "@conventional-changelog/template": "^1.2.0" }, "engines": { - "node": ">=18" + "node": ">=22" } }, "node_modules/conventional-changelog-writer": { @@ -12543,9 +12652,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.5.tgz", + "integrity": "sha512-F7JnSfPl5ASt6LqwWyUQ3T8BwN3q0eQEbFMYa2iRWaVQmmudo0d7fRmwM4O002gsvW1bs0yBYioutsAjqLJMvQ==", "dev": true, "license": "MIT", "dependencies": { From ca72bf7ac32f40643f8b34b7bcafe15675f07226 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 07:59:24 +0000 Subject: [PATCH 02/34] fix(deps): update dependency graphql to v16.14.2 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a358f03..6393a1f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "commander": "14.0.3", - "graphql": "16.12.0", + "graphql": "16.14.2", "node-emoji": "2.2.0" }, "bin": { @@ -6818,9 +6818,9 @@ "license": "ISC" }, "node_modules/graphql": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", - "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", + "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" diff --git a/package.json b/package.json index 3d505686..73aeee04 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "homepage": "https://github.com/linearis-oss/linearis#readme", "dependencies": { "commander": "14.0.3", - "graphql": "16.12.0", + "graphql": "16.14.2", "node-emoji": "2.2.0" }, "devDependencies": { From 0375ef9888c47f8c38e643f1155621d0e3c6a07c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 6 Jul 2026 08:02:20 +0000 Subject: [PATCH 03/34] chore(release): 2026.7.0-next.1 [skip ci] ## [2026.7.0-next.1](https://github.com/linearis-oss/linearis/compare/v2026.6.0...v2026.7.0-next.1) (2026-07-06) --- CHANGELOG.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d2cbfcf..c864054d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [2026.7.0-next.1](https://github.com/linearis-oss/linearis/compare/v2026.6.0...v2026.7.0-next.1) (2026-07-06) + ## [2026.6.0](https://github.com/linearis-oss/linearis/compare/v2026.5.0...v2026.6.0) (2026-07-04) ### Features diff --git a/package-lock.json b/package-lock.json index 6393a1f0..25813130 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "linearis", - "version": "2026.6.0", + "version": "2026.7.0-next.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "linearis", - "version": "2026.6.0", + "version": "2026.7.0-next.1", "license": "MIT", "dependencies": { "commander": "14.0.3", diff --git a/package.json b/package.json index 73aeee04..6b414969 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linearis", - "version": "2026.6.0", + "version": "2026.7.0-next.1", "description": "CLI tool for Linear.app with JSON output, smart ID resolution, and optimized GraphQL queries. Designed for LLM agents and humans who prefer structured data.", "main": "dist/main.js", "type": "module", From 290424ae2a7f53d3b014de822187429d4fc350b6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 08:03:22 +0000 Subject: [PATCH 04/34] fix(deps): update dependency commander to v15 --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 25813130..9b986211 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2026.7.0-next.1", "license": "MIT", "dependencies": { - "commander": "14.0.3", + "commander": "15.0.0", "graphql": "16.14.2", "node-emoji": "2.2.0" }, @@ -5636,12 +5636,12 @@ "license": "MIT" }, "node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.12.0" } }, "node_modules/common-tags": { diff --git a/package.json b/package.json index 6b414969..1930a8b3 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ }, "homepage": "https://github.com/linearis-oss/linearis#readme", "dependencies": { - "commander": "14.0.3", + "commander": "15.0.0", "graphql": "16.14.2", "node-emoji": "2.2.0" }, From cc2a817e5ff1b30b852157799e1c718e907e0c7a Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 6 Jul 2026 09:00:50 +0000 Subject: [PATCH 05/34] chore(release): 2026.7.0-next.2 [skip ci] ## [2026.7.0-next.2](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.1...v2026.7.0-next.2) (2026-07-06) --- CHANGELOG.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c864054d..1edcfe14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [2026.7.0-next.2](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.1...v2026.7.0-next.2) (2026-07-06) + ## [2026.7.0-next.1](https://github.com/linearis-oss/linearis/compare/v2026.6.0...v2026.7.0-next.1) (2026-07-06) ## [2026.6.0](https://github.com/linearis-oss/linearis/compare/v2026.5.0...v2026.6.0) (2026-07-04) diff --git a/package-lock.json b/package-lock.json index 9b986211..0c434079 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "linearis", - "version": "2026.7.0-next.1", + "version": "2026.7.0-next.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "linearis", - "version": "2026.7.0-next.1", + "version": "2026.7.0-next.2", "license": "MIT", "dependencies": { "commander": "15.0.0", diff --git a/package.json b/package.json index 1930a8b3..3210fcc1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linearis", - "version": "2026.7.0-next.1", + "version": "2026.7.0-next.2", "description": "CLI tool for Linear.app with JSON output, smart ID resolution, and optimized GraphQL queries. Designed for LLM agents and humans who prefer structured data.", "main": "dist/main.js", "type": "module", From 2c95d41a5f107f6eada1f67db03b84341034e4d1 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:08:49 +0200 Subject: [PATCH 06/34] docs(readme): add GraphQL coverage matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README claimed "broad domain coverage" and mentioned a coverage trade-off in passing, but gave no way to tell whether a specific operation was supported. Users and agents had to discover gaps by running a command and getting nothing back. Diffed the Linear GraphQL schema (164 queries, 373 mutations) against the root fields referenced in graphql/{queries,mutations} and against the commands registered in src/commands, then generalised the result to domain level. The "### Domains" table is replaced by a "## Coverage" section holding a single table over the entire surface: each area carries an extent rating (complete / core-with-gaps / narrow slice / no CLI surface) alongside what it supports and what it does not. Two deliberate choices: - One table, not two. Splitting covered from uncovered buried the uncovered half below the fold and let a reader mistake the first table for the whole surface. Rating uncovered areas red in the same table makes the shape of the gap legible at a glance. - Domain granularity, not per-operation. A raw root-field checklist would be 537 rows, would understate coverage (several commands read via nested fields rather than root queries), and would need updating on every schema refresh. The domain view answers the question a user actually has — "can I do X with this?" — and stays accurate longer. Every count in the section is exact rather than rounded, since a rounded headline next to exact sub-counts reads as an arithmetic error. The integrations row counts root fields whose name contains "integration" against .context/linear-schema.graphql: 65 mutations and 8 queries, so 73 root fields. That boundary is a judgement call — a looser match that also swept in attachmentLink* and issueImport* would give 83, but those fields are already accounted for in the attachments and imports rows, so counting them here would double-count. The name-match definition keeps the rows disjoint. Rows state verb lists rather than CRUD shorthand where the shorthand would over-claim: initiative updates have no delete mutation in the API, and project labels are readable (`labels list --type project`) but not creatable from the CLI, while project label *assignment* does work via `projects create/update --labels`. Docs-only change; the build/test checklist does not apply. --- README.md | 56 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index dd2c1027..5729977d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The official Linear MCP works well, but it costs ~13k tokens just by being conne A typical agent interaction costs **~500–700 tokens** of context instead of ~13k. The agent pays only for what it uses, one domain at a time. > [!NOTE] -> The trade-off is coverage. Linearis focuses on the operations that matter for day-to-day work — issues, discussions, cycles, projects, documents, and files. For custom workflows, integrations, or workspace settings, the MCP is the better choice. +> The trade-off is coverage. Linearis focuses on the operations that matter for day-to-day work — issues, discussions, cycles, projects, documents, and files. For custom workflows, integrations, or workspace settings, the MCP is the better choice. See [Coverage](#coverage) for the exact picture. ## Features @@ -108,22 +108,44 @@ linearis issues replies linearis issues reply --body "I found the root cause" ``` -### Domains - -| Domain | What it covers | -|---|---| -| `issues` | Work items with status, priority, assignee, labels, and discussions | -| `projects` | Groups of issues working toward a goal | -| `initiatives` | Strategic, multi-project goals | -| `cycles` | Time-boxed iterations (sprints) per team | -| `milestones` | Progress checkpoints within projects | -| `documents` | Long-form markdown docs attached to projects or issues | -| `labels` | Categorization tags for issues and projects | -| `attachments` | Linked external resources on issues (PRs, commits, URLs) | -| `files` | Upload and download file attachments | -| `teams` | Organizational units owning issues and cycles | -| `users` | Workspace members and assignees | -| `auth` | Authenticate with the Linear API | +## Coverage + +Linear's GraphQL API exposes **537 root operations** (164 queries, 373 mutations). Linearis wires about **75 of them** directly, plus a number of nested reads — chosen to cover planning and issue work end to end rather than the whole API. + +The table below is the honest picture of the whole surface — what works today, and what you'll need the [Linear MCP](#linearis-vs-linear-mcp) or a raw API call for. + +**Legend** — ✅ complete for practical purposes · 🟡 core operations, known gaps · 🟠 read-only or narrow slice · 🔴 no CLI surface yet + +| Area | Extent | What you can do | Not covered | +|---|---|---|---| +| `auth` | ✅ | Interactive login, token status, logout | — | +| Discussions | ✅ | Root threads and replies on issues, projects, and initiatives; edit, delete, resolve/unresolve; emoji reactions on any of them | Custom workspace emoji management | +| `issues` | 🟡 | List, filter, full-text search, read, create, update, archive/unarchive, delete; assign labels/assignee/state/priority/project/cycle; relations (list/add/remove); activity history | Batch create/update, subscribe/unsubscribe, share links, reminders, external sync toggles | +| `initiatives` | 🟡 | List, read, create, update, archive/unarchive, delete; attach/detach projects; initiative-to-initiative relations; initiative updates (list, read, create, update, archive/unarchive); discussions | Initiative labels, lead-team reassignment, relation reordering | +| `projects` | 🟡 | List, read, create, update, archive/unarchive, delete; assign project labels by name (`--labels`, `--label-mode`, `--clear-labels`); discussions | Project updates (status posts), project-label CRUD, project relations, project status administration, Slack channel creation | +| `documents` | 🟡 | List, read, create, update, delete | Content history, document full-text search, unarchive | +| `milestones` | 🟡 | List, read, create, update (per project) | Delete, reordering/move between projects | +| `attachments` | 🟡 | List on an issue, create from a URL, delete | Update, and the provider-specific link mutations (GitHub PR/issue, GitLab MR, Slack, Jira, Zendesk, Intercom, Front, Salesforce, Discord) | +| `files` | 🟡 | Upload a file, download via signed URL | Delete uploads, image-from-URL, CSV export reports | +| `teams` | 🟡 | List, read, create, update; list/add/remove members | Delete, workflow-state administration, triage responsibility, git automation, SLA configuration | +| `labels` | 🟠 | Issue labels: list, read, create, update, delete; project labels: list (`--type project`) | Project-label create/update/delete, initiative labels, retire/restore | +| `cycles` | 🟠 | List cycles, read a cycle with its issues | Create, update, archive, shift all, start upcoming cycle | +| `users` | 🟠 | List workspace members | Read a single user, update, role changes, suspend/unsuspend, user settings, session management | +| Integrations | 🔴 | — | All 73 integration root fields (65 mutations, 8 queries): Slack, GitHub, GitLab, Jira, Figma, Sentry, PagerDuty, Intercom, Salesforce, and more | +| Organization & admin | 🔴 | — | Org settings, invites, domains, webhooks, OAuth apps, audit log, SSO | +| Releases | 🔴 | — | Releases, release pipelines, stages, release notes | +| Customers (CRM) | 🔴 | — | Customers, needs, tiers, customer statuses | +| Views & templates | 🔴 | — | Custom views, favorites, templates, view preferences | +| Notifications | 🔴 | — | Inbox, subscriptions, snooze, mark read, push subscriptions | +| Agent sessions | 🔴 | — | Agent sessions, activities, skills, semantic search | +| Roadmaps | 🔴 | — | Roadmaps and roadmap-to-project links | +| Imports & exports | 🔴 | — | Jira/Asana/Clubhouse/GitHub/CSV import jobs | +| Schedules | 🔴 | — | Time schedules and on-call rotations | + +The 🔴 rows are mostly workspace administration and integration plumbing — work an agent rarely does mid-task, and the main reason the API-wide coverage number is low while day-to-day coverage is not. + +> [!TIP] +> Missing something you need day to day? [Open an issue](https://github.com/linearis-oss/linearis/issues) — the covered set is driven by what people actually reach for, not by API completeness. ## AI agent integration From 8b761d93a6e692f5f112fec4ec657d4b640a7985 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 08:15:26 +0000 Subject: [PATCH 07/34] chore(deps): update dependency typescript to v7 --- package-lock.json | 375 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 369 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c434079..4c5df31b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "lefthook": "^2.1.0", "semantic-release": "^25.0.1", "tsx": "^4.20.5", - "typescript": "^6.0.0", + "typescript": "^7.0.0", "vitest": "^4.0.0" }, "engines": { @@ -4765,6 +4765,346 @@ "@types/node": "*" } }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@vitest/coverage-v8": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz", @@ -12694,17 +13034,38 @@ } }, "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", "dev": true, "license": "Apache-2.0", "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "tsc": "bin/tsc" }, "engines": { - "node": ">=14.17" + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" } }, "node_modules/uglify-js": { diff --git a/package.json b/package.json index 3210fcc1..380da93a 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "lefthook": "^2.1.0", "semantic-release": "^25.0.1", "tsx": "^4.20.5", - "typescript": "^6.0.0", + "typescript": "^7.0.0", "vitest": "^4.0.0" }, "graphql": { From 3ee16ebf646dd2d9449f4526c05fb4639cfd65ab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 08:50:46 +0000 Subject: [PATCH 08/34] chore(deps): update actions/setup-node action to v7 --- .github/actions/setup/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index e6738a78..b719e9a3 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -27,7 +27,7 @@ runs: # node-version (when set by a matrix job) takes precedence over # node-version-file; an empty node-version falls back to .nvmrc. - name: Setup Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ inputs.node-version }} node-version-file: .nvmrc From 220e7b607b707bbc477a6ac9f1fc3a3985446aa8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 08:54:52 +0000 Subject: [PATCH 09/34] chore(deps): update github actions --- .github/workflows/ci-post-merge.yml | 2 +- .github/workflows/ci-validate.yml | 16 ++++++++-------- .github/workflows/knip-comment.yml | 2 +- .../workflows/release-promote-next-to-main.yml | 2 +- .github/workflows/release-publish.yml | 2 +- .../workflows/release-sync-main-back-to-next.yml | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci-post-merge.yml b/.github/workflows/ci-post-merge.yml index eaa7fa25..0242f079 100644 --- a/.github/workflows/ci-post-merge.yml +++ b/.github/workflows/ci-post-merge.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup and build uses: ./.github/actions/setup diff --git a/.github/workflows/ci-validate.yml b/.github/workflows/ci-validate.yml index e9a07004..443990a2 100644 --- a/.github/workflows/ci-validate.yml +++ b/.github/workflows/ci-validate.yml @@ -34,7 +34,7 @@ jobs: node-version: [22, 24] steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup and build uses: ./.github/actions/setup @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup and build uses: ./.github/actions/setup @@ -68,7 +68,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Run actionlint uses: docker://rhysd/actionlint@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 # v1.7.12 @@ -90,7 +90,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # Build runs codegen (prebuild) so src/gql exists and imports resolve. - name: Setup and build @@ -162,7 +162,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 @@ -185,7 +185,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # No build here: verify:packed-binaries runs `npm pack`, whose `prepack` # builds (and generates) the package the same way a consumer install would. @@ -205,7 +205,7 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Check for docs/plans/*.md in branch history @@ -253,7 +253,7 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 diff --git a/.github/workflows/knip-comment.yml b/.github/workflows/knip-comment.yml index bab79f04..08045f2f 100644 --- a/.github/workflows/knip-comment.yml +++ b/.github/workflows/knip-comment.yml @@ -47,7 +47,7 @@ jobs: run: echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT" - name: Post / update PR comment - uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4 + uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 with: header: knip-dead-code path: knip-comment.md diff --git a/.github/workflows/release-promote-next-to-main.yml b/.github/workflows/release-promote-next-to-main.yml index 175daa2b..50b07384 100644 --- a/.github/workflows/release-promote-next-to-main.yml +++ b/.github/workflows/release-promote-next-to-main.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: next fetch-depth: 0 diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 47510de9..e32e64fe 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -65,7 +65,7 @@ jobs: echo "Releasing from branch: $branch" - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 ref: ${{ steps.target.outputs.branch }} diff --git a/.github/workflows/release-sync-main-back-to-next.yml b/.github/workflows/release-sync-main-back-to-next.yml index 569b09ce..91b980f7 100644 --- a/.github/workflows/release-sync-main-back-to-next.yml +++ b/.github/workflows/release-sync-main-back-to-next.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout next - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: next fetch-depth: 0 From 9a5ca7529efdec30d95c77838f947f6e7255b225 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:29:27 +0200 Subject: [PATCH 10/34] feat(issues): add --clear-assignee and --clear-project to issues update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `issues update` could set an assignee or a project but never remove one. Both fields were assembled behind a truthiness guard (`if (ids.assigneeId)`), so no `assigneeId`/`projectId` key ever reached `issueUpdate` when the user wanted them gone, and there was no CLI spelling that produced one. For the agent-orchestration use case in #282 — assignee as a soft ownership lock — a lock you can take but never release is not a lock. Add `--clear-assignee` and `--clear-project`, each sending an explicit `null` to the Linear API. This follows the convention already established by the six existing clear flags on this command (`--clear-labels`, `--clear-due-date`, `--clear-estimate`, `--clear-cycle`, `--clear-parent-ticket`, `--clear-project-milestone`): reject the setter and the clearer together, skip ID resolution when clearing so no pointless resolver round-trip happens, and assemble the input with if/else-if. Because a project milestone belongs to a project, `--clear-project` nulls `projectMilestoneId` in the same mutation and rejects `--project-milestone` up front. Detaching the project while keeping the milestone would leave the issue pointing at a milestone of a project it no longer belongs to, and that state is silent — the issue looks fine until someone reads the milestone. Requiring the caller to pass `--clear-project --clear-project-milestone` was considered and rejected: refusing the contradictory combination at parse time while allowing it to arise implicitly is inconsistent. Moving an issue to a *different* project with `--project` is left alone; the new milestone is the caller's to pick. The alternative considered was a sentinel value such as `--assignee ""`. It was rejected: it is ambiguous against a legitimately empty argument, invisible in `--help`, and inconsistent with the existing clear flags. No service or GraphQL change was needed — `UpdateIssueInput` picks these fields from the codegen `IssueUpdateInput`, where they are already nullable. Closes #282 --- src/commands/issues.ts | 46 ++++++++- tests/unit/commands/issues.test.ts | 160 +++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 5 deletions(-) diff --git a/src/commands/issues.ts b/src/commands/issues.ts index 9e37b872..f567365b 100644 --- a/src/commands/issues.ts +++ b/src/commands/issues.ts @@ -121,7 +121,9 @@ interface UpdateOptions { estimate?: string; clearEstimate?: boolean; assignee?: string; + clearAssignee?: boolean; project?: string; + clearProject?: boolean; labels?: string; labelMode?: string; clearLabels?: boolean; @@ -1282,7 +1284,9 @@ export function setupIssuesCommands(program: Command): void { .option("--status ", "new status") .option("--priority <1-4>", "new priority") .option("--assignee ", "new assignee") + .option("--clear-assignee", "clear assignee") .option("--project ", "new project") + .option("--clear-project", "clear project (also clears project milestone)") .option("--labels ", "labels to apply (comma-separated)") .option("--label-mode ", "add | remove | overwrite") .option("--clear-labels", "remove all labels") @@ -1305,6 +1309,24 @@ export function setupIssuesCommands(program: Command): void { .action( commandAction<[string, UpdateOptions, Command]>( async (issue, options, command) => { + if (options.assignee && options.clearAssignee) { + throw new Error( + "Cannot use --assignee and --clear-assignee together", + ); + } + + if (options.project && options.clearProject) { + throw new Error( + "Cannot use --project and --clear-project together", + ); + } + + if (options.clearProject && options.projectMilestone) { + throw new Error( + "Cannot use --clear-project and --project-milestone together", + ); + } + if (options.parentTicket && options.clearParentTicket) { throw new Error( "Cannot use --parent-ticket and --clear-parent-ticket together", @@ -1406,8 +1428,12 @@ export function setupIssuesCommands(program: Command): void { } const updIdsInput: ResolveUpdateIssueIdsInput = {}; - if (options.assignee) updIdsInput.assignee = options.assignee; - if (options.project) updIdsInput.project = options.project; + if (!options.clearAssignee && options.assignee) { + updIdsInput.assignee = options.assignee; + } + if (!options.clearProject && options.project) { + updIdsInput.project = options.project; + } if (!options.clearLabels && options.labels) { updIdsInput.labels = options.labels.split(",").map((l) => l.trim()); } @@ -1459,11 +1485,15 @@ export function setupIssuesCommands(program: Command): void { input.estimate = parsedEstimate; } - if (ids.assigneeId) { + if (options.clearAssignee) { + input.assigneeId = null; + } else if (ids.assigneeId) { input.assigneeId = ids.assigneeId; } - if (ids.projectId) { + if (options.clearProject) { + input.projectId = null; + } else if (ids.projectId) { input.projectId = ids.projectId; } @@ -1495,7 +1525,13 @@ export function setupIssuesCommands(program: Command): void { input.parentId = ids.parentId; } - if (options.clearProjectMilestone) { + // A milestone belongs to a project, so --clear-project must detach the + // milestone too — otherwise the issue keeps pointing at a milestone of + // a project it no longer belongs to. Moving the issue to a different + // project with --project is deliberately not handled here: the new + // milestone is the caller's to pick, and reconciling the old one is + // left to the API rather than guessed at locally. + if (options.clearProjectMilestone || options.clearProject) { input.projectMilestoneId = null; } else if (ids.projectMilestoneId) { input.projectMilestoneId = ids.projectMilestoneId; diff --git a/tests/unit/commands/issues.test.ts b/tests/unit/commands/issues.test.ts index f5683794..a160b6e3 100644 --- a/tests/unit/commands/issues.test.ts +++ b/tests/unit/commands/issues.test.ts @@ -1098,6 +1098,166 @@ describe("issues update --assignee", () => { }); }); +describe("issues update --clear-assignee", () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.spyOn(console, "log").mockImplementation(() => {}); + vi.spyOn(console, "error").mockImplementation(() => {}); + vi.spyOn(process, "exit").mockImplementation(() => undefined as never); + }); + + it("sends an explicit null assigneeId", async () => { + const program = createProgram(); + await program.parseAsync([ + "node", + "test", + "issues", + "update", + "ENG-42", + "--clear-assignee", + ]); + + expect(updateIssue).toHaveBeenCalledWith( + expect.anything(), + "resolved-issue-uuid", + expect.objectContaining({ assigneeId: null }), + ); + }); + + it("does not resolve an assignee when clearing alongside other fields", async () => { + const program = createProgram(); + await program.parseAsync([ + "node", + "test", + "issues", + "update", + "ENG-42", + "--clear-assignee", + "--status", + "Todo", + ]); + + expect(resolveUpdateIssueIds).toHaveBeenCalledWith( + expect.anything(), + expect.not.objectContaining({ assignee: expect.anything() }), + expect.anything(), + ); + }); + + it("rejects --assignee and --clear-assignee together", async () => { + const program = createProgram(); + await program.parseAsync([ + "node", + "test", + "issues", + "update", + "ENG-42", + "--assignee", + "Jane Smith", + "--clear-assignee", + ]); + + expect(console.error).toHaveBeenCalledWith( + expect.stringContaining( + "Cannot use --assignee and --clear-assignee together", + ), + ); + expect(process.exit).toHaveBeenCalledWith(1); + expect(updateIssue).not.toHaveBeenCalled(); + }); +}); + +describe("issues update --clear-project", () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.spyOn(console, "log").mockImplementation(() => {}); + vi.spyOn(console, "error").mockImplementation(() => {}); + vi.spyOn(process, "exit").mockImplementation(() => undefined as never); + }); + + it("sends an explicit null projectId and clears the project milestone", async () => { + const program = createProgram(); + await program.parseAsync([ + "node", + "test", + "issues", + "update", + "ENG-42", + "--clear-project", + ]); + + expect(updateIssue).toHaveBeenCalledWith( + expect.anything(), + "resolved-issue-uuid", + expect.objectContaining({ projectId: null, projectMilestoneId: null }), + ); + }); + + it("does not resolve a project when clearing alongside other fields", async () => { + const program = createProgram(); + await program.parseAsync([ + "node", + "test", + "issues", + "update", + "ENG-42", + "--clear-project", + "--status", + "Todo", + ]); + + expect(resolveUpdateIssueIds).toHaveBeenCalledWith( + expect.anything(), + expect.not.objectContaining({ project: expect.anything() }), + expect.anything(), + ); + }); + + it("rejects --project and --clear-project together", async () => { + const program = createProgram(); + await program.parseAsync([ + "node", + "test", + "issues", + "update", + "ENG-42", + "--project", + "Apollo", + "--clear-project", + ]); + + expect(console.error).toHaveBeenCalledWith( + expect.stringContaining( + "Cannot use --project and --clear-project together", + ), + ); + expect(process.exit).toHaveBeenCalledWith(1); + expect(updateIssue).not.toHaveBeenCalled(); + }); + + it("rejects --clear-project and --project-milestone together", async () => { + const program = createProgram(); + await program.parseAsync([ + "node", + "test", + "issues", + "update", + "ENG-42", + "--clear-project", + "--project-milestone", + "Beta", + ]); + + expect(console.error).toHaveBeenCalledWith( + expect.stringContaining( + "Cannot use --clear-project and --project-milestone together", + ), + ); + expect(process.exit).toHaveBeenCalledWith(1); + expect(updateIssue).not.toHaveBeenCalled(); + }); +}); + describe("issues read", () => { beforeEach(() => { vi.clearAllMocks(); From fb401ea029bd559140567ea61c6ff1493609b3ea Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:29:31 +0200 Subject: [PATCH 11/34] feat(initiatives): add --clear-owner to initiatives update `initiatives update --owner` had the same write-only shape as the issue assignee. The owner could be set but never removed, because `ownerId` was only ever written into the update input when `--owner` was present. Add `--clear-owner`, which writes an explicit `null` into `UpdateInitiativeInput`. The resolver call is skipped when clearing, so no user lookup is performed for a value that is being discarded, and passing `--owner` together with `--clear-owner` is rejected up front. The error uses `invalidParameterError` rather than the plain `Error` that `issues.ts` throws for the same class of conflict. This file and `projects.ts` consistently use the former, so the local convention wins over cross-file uniformity. `--clear-owner` on its own satisfies the existing "at least one option must be provided" guard, since it writes a key into the input object. Refs #282 --- src/commands/initiatives/entity.ts | 13 +++++++- tests/unit/commands/initiatives.test.ts | 41 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/commands/initiatives/entity.ts b/src/commands/initiatives/entity.ts index 8466ef57..e6f9671f 100644 --- a/src/commands/initiatives/entity.ts +++ b/src/commands/initiatives/entity.ts @@ -185,6 +185,7 @@ interface InitiativeUpdateOptions { description?: string; content?: string; owner?: string; + clearOwner?: boolean; status?: string; targetDate?: string; sortOrder?: string; @@ -734,12 +735,20 @@ export function setupInitiativeEntityCommands(initiatives: Command): void { .option("--description ", "new description") .option("--content ", "new content (markdown)") .option("--owner ", "new owner (name, email, or UUID)") + .option("--clear-owner", "clear owner") .option("--status ", "planned, active, completed") .option("--target-date ", "new target date (YYYY-MM-DD)") .option("--sort-order ", "new display sort order") .action( commandAction<[string, InitiativeUpdateOptions, Command]>( async (initiative, options, command) => { + if (options.owner && options.clearOwner) { + throw invalidParameterError( + "--owner", + "cannot be combined with --clear-owner", + ); + } + const ctx = createContext(getRootOpts(command)); const initiativeId = await resolveInitiativeId(ctx.gql, initiative); @@ -757,7 +766,9 @@ export function setupInitiativeEntityCommands(initiatives: Command): void { input.content = options.content; } - if (options.owner) { + if (options.clearOwner) { + input.ownerId = null; + } else if (options.owner) { input.ownerId = await resolveUserId(ctx.gql, options.owner); } diff --git a/tests/unit/commands/initiatives.test.ts b/tests/unit/commands/initiatives.test.ts index bd0b7d6e..51787872 100644 --- a/tests/unit/commands/initiatives.test.ts +++ b/tests/unit/commands/initiatives.test.ts @@ -398,6 +398,47 @@ describe("initiatives update", () => { expect.stringContaining("at least one option must be provided"), ); }); + + it("clears the owner with --clear-owner", async () => { + const program = createProgram(); + + await program.parseAsync([ + "node", + "test", + "initiatives", + "update", + "Growth", + "--clear-owner", + ]); + + expect(resolveUserId).not.toHaveBeenCalled(); + expect(updateInitiative).toHaveBeenCalledWith( + expect.anything(), + "resolved-initiative-uuid", + expect.objectContaining({ ownerId: null }), + ); + }); + + it("rejects --owner and --clear-owner together", async () => { + const program = createProgram(); + + await program.parseAsync([ + "node", + "test", + "initiatives", + "update", + "Growth", + "--owner", + "Alice", + "--clear-owner", + ]); + + expect(updateInitiative).not.toHaveBeenCalled(); + expect(console.error).toHaveBeenCalledWith( + expect.stringContaining("cannot be combined with --clear-owner"), + ); + expect(process.exit).toHaveBeenCalledWith(1); + }); }); describe("initiative relations and projects wiring", () => { From b9310a090049cce5a7763eae824430298b2ab1c4 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 6 Aug 2026 10:39:30 +0000 Subject: [PATCH 12/34] chore(release): 2026.7.0-next.3 [skip ci] ## [2026.7.0-next.3](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.2...v2026.7.0-next.3) (2026-08-06) --- CHANGELOG.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1edcfe14..2b4e730e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [2026.7.0-next.3](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.2...v2026.7.0-next.3) (2026-08-06) + ## [2026.7.0-next.2](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.1...v2026.7.0-next.2) (2026-07-06) ## [2026.7.0-next.1](https://github.com/linearis-oss/linearis/compare/v2026.6.0...v2026.7.0-next.1) (2026-07-06) diff --git a/package-lock.json b/package-lock.json index 4c5df31b..d9d49650 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "linearis", - "version": "2026.7.0-next.2", + "version": "2026.7.0-next.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "linearis", - "version": "2026.7.0-next.2", + "version": "2026.7.0-next.3", "license": "MIT", "dependencies": { "commander": "15.0.0", diff --git a/package.json b/package.json index 380da93a..f54a0bc6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linearis", - "version": "2026.7.0-next.2", + "version": "2026.7.0-next.3", "description": "CLI tool for Linear.app with JSON output, smart ID resolution, and optimized GraphQL queries. Designed for LLM agents and humans who prefer structured data.", "main": "dist/main.js", "type": "module", From 3bd0e3899d64bfa5195009d686cf2cbdddc4ed06 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:21:55 +0200 Subject: [PATCH 13/34] fix(cli): emit JSON envelope for argument-parse errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linearis promises JSON on every non-help path and uses a distinct exit code (42) to make authentication failures programmatically detectable. Argument-parse failures honoured neither: Commander wrote plain text to stderr and exited 1 — the same code a legitimate "entity not found" uses. An agent could not tell "I called the CLI wrong" from "the issue does not exist", and the plain-text message carried no recovery path. Root cause of the worst case: the root command and every domain register an action handler (the overview / domain help) while declaring no arguments. With that combination Commander routes an unrecognised operand to `_excessArguments()` rather than `unknownCommand()`, so `linearis issues get ABC-123` reported "too many arguments for 'issues'" instead of naming the unknown subcommand, and `.showSuggestionAfterError()` could never fire. `interceptParseErrors()` walks the finished command tree and installs a per-command `exitOverride`, so each callback closes over the command that failed. That yields the exact failing scope without re-parsing argv — the alternative would need option-arity heuristics to tell an option value from a subcommand name. `Command.error()` writes its message to stderr *before* calling `_exit`, so `writeErr` is suppressed too; the same text is still available on `CommanderError.message` and is reused verbatim (minus the `error: ` prefix) for every code except UNKNOWN_COMMAND, which is rephrased to name the token. Two codes need their own phrasing rather than that fallback. UNKNOWN_COMMAND names the offending token. And `commander.help` — raised with exit code 1, so it does not take the exit-0 passthrough, whenever a command has subcommands, no action handler and no operand to dispatch on (`issues threads`, `projects threads`, `initiatives threads`, all built by `addCommentReactionCommands`) — carries Commander's internal `(outputHelp)` placeholder as its message, which means nothing to a caller. It becomes `Missing subcommand for ""` routed to the "list valid subcommands" instruction, since `available_commands` is populated for those groups. Exit code 2 was chosen for malformed invocation: it is the conventional Unix usage-error code, and it keeps 1 meaning "the request was well-formed but could not be fulfilled". `available_commands` is read from `cmd.commands`, the same source `formatDomainUsage` uses, so it cannot drift from USAGE.md, and `instruction` resolves to the nearest ancestor owning a `usage` subcommand. Help, version, and bare `linearis` / `linearis ` throw a CommanderError with exit code 0 after writing to stdout; those pass through untouched, so all existing successful output is byte-identical. Also fixes an unreported defect on the same line: `program.parseAsync()` was neither awaited nor caught, so anything rejecting during parse surfaced as a raw Node unhandled-rejection stack trace. `handleParseFailure` is now the terminal handler for that promise. Closes #281 --- src/common/cli-errors.ts | 191 +++++++++++++++ src/common/errors.ts | 7 + src/common/output.ts | 11 + src/main.ts | 9 +- tests/unit/common/cli-errors.test.ts | 339 +++++++++++++++++++++++++++ tests/unit/common/output.test.ts | 27 +++ 6 files changed, 583 insertions(+), 1 deletion(-) create mode 100644 src/common/cli-errors.ts create mode 100644 tests/unit/common/cli-errors.test.ts diff --git a/src/common/cli-errors.ts b/src/common/cli-errors.ts new file mode 100644 index 00000000..01c861ec --- /dev/null +++ b/src/common/cli-errors.ts @@ -0,0 +1,191 @@ +import type { Command, CommanderError } from "commander"; +import { AuthenticationError, USAGE_ERROR_CODE } from "./errors.js"; +import { outputAuthError, outputError, outputUsageError } from "./output.js"; + +type UsageErrorCode = + | "UNKNOWN_COMMAND" + | "UNKNOWN_OPTION" + | "MISSING_ARGUMENT" + | "TOO_MANY_ARGUMENTS" + | "INVALID_USAGE"; + +export interface UsageErrorPayload { + error: UsageErrorCode; + message: string; + /** Space-joined path of the command that failed to parse, e.g. "linearis issues". */ + command: string; + /** Present only when the failing scope has subcommands to choose from. */ + available_commands?: string[]; + instruction: string; + exit_code: number; +} + +/** + * Pairs a Commander parse failure with the `Command` that raised it. + * + * Commander's `CommanderError` carries only a message and a code, so the scope + * of the failure ("which command was being parsed?") would otherwise have to be + * reconstructed from `argv` — which needs option-arity heuristics to tell an + * option value from a subcommand name. Capturing the `Command` at throw time in + * the per-command `exitOverride` callback avoids that entirely. + * + * Module-private: it never escapes {@link handleParseFailure}. + */ +class CliUsageError extends Error { + readonly commanderError: CommanderError; + readonly command: Command; + + constructor(commanderError: CommanderError, command: Command) { + super(commanderError.message); + this.name = "CliUsageError"; + this.commanderError = commanderError; + this.command = command; + } +} + +/** "linearis issues read" for a leaf command, walking up via `parent`. */ +function commandPath(command: Command): string { + const parts: string[] = []; + let current: Command | null = command; + while (current) { + parts.unshift(current.name()); + current = current.parent; + } + return parts.join(" "); +} + +/** + * Nearest command (self or ancestor) that owns a `usage` subcommand. Every + * domain registers one and so does the root, so this always resolves to a + * runnable suggestion. + */ +function usageScope(command: Command): Command { + let current: Command | null = command; + while (current) { + if (current.commands.some((c) => c.name() === "usage")) return current; + current = current.parent; + } + return command; +} + +function classify(error: CommanderError, command: Command): UsageErrorCode { + switch (error.code) { + case "commander.unknownCommand": + return "UNKNOWN_COMMAND"; + case "commander.excessArguments": + // A domain command (`issues`) declares no arguments but has subcommands, + // and its overview action handler makes Commander treat an unrecognised + // subcommand as an excess operand rather than an unknown command. + return command.commands.length > 0 && + command.registeredArguments.length === 0 + ? "UNKNOWN_COMMAND" + : "TOO_MANY_ARGUMENTS"; + case "commander.unknownOption": + return "UNKNOWN_OPTION"; + case "commander.missingArgument": + return "MISSING_ARGUMENT"; + default: + return "INVALID_USAGE"; + } +} + +/** The first operand Commander could not account for, if there is one. */ +function offendingToken( + error: CommanderError, + command: Command, +): string | undefined { + return error.code === "commander.unknownCommand" + ? command.args[0] + : command.args[command.registeredArguments.length]; +} + +/** + * Turn a Commander parse failure into the JSON envelope the CLI contract + * promises. Pure — exported for tests. + */ +export function describeUsageError( + error: CommanderError, + command: Command, +): UsageErrorPayload { + const code = classify(error, command); + const path = commandPath(command); + const usagePath = commandPath(usageScope(command)); + const token = offendingToken(error, command); + + // Commander raises `commander.help` with exit code 1 when a group has + // subcommands, no action handler and no operand to dispatch on (`linearis + // issues threads`). Its message is the internal "(outputHelp)" placeholder, + // so it needs its own phrasing rather than the generic message fallback. + const missingSubcommand = error.code === "commander.help"; + + const message = + code === "UNKNOWN_COMMAND" && token !== undefined + ? `Unknown command "${token}" for "${path}".` + : missingSubcommand + ? `Missing subcommand for "${path}".` + : error.message.replace(/^error: /, ""); + + const instruction = + code === "UNKNOWN_COMMAND" || missingSubcommand + ? `Run '${usagePath} usage' to list valid subcommands.` + : `Run '${usagePath} usage' to see the valid arguments and options.`; + + const available = command.commands.map((c) => c.name()); + + return { + error: code, + message, + command: path, + ...(available.length > 0 ? { available_commands: available } : {}), + instruction, + exit_code: USAGE_ERROR_CODE, + }; +} + +/** + * Install per-command exit callbacks so a parse failure surfaces as a + * `CliUsageError` carrying its `Command`, and silence Commander's plain-text + * stderr writes. + * + * `Command.error()` writes the message to stderr *before* calling `_exit`, so + * suppressing `writeErr` is what keeps stderr JSON-only; the same text is still + * available on `CommanderError.message`. + * + * Must be called after every command has been registered, so the walk sees the + * whole tree. + */ +export function interceptParseErrors(program: Command): void { + const install = (cmd: Command): void => { + cmd.exitOverride((err) => { + throw new CliUsageError(err, cmd); + }); + cmd.configureOutput({ writeErr: () => {} }); + for (const child of cmd.commands) install(child); + }; + install(program); +} + +/** + * Terminal handler for anything escaping `program.parseAsync()`. Before this + * existed the promise was neither awaited nor caught, so a rejection during + * parsing surfaced as a raw Node unhandled-rejection stack trace. + */ +export function handleParseFailure(error: unknown): void { + if (error instanceof CliUsageError) { + // `--help`, `--version` and a bare `linearis ` throw with exit code + // 0 after writing to stdout: success, not a usage error. + if (error.commanderError.exitCode === 0) { + process.exit(0); + return; + } + outputUsageError(describeUsageError(error.commanderError, error.command)); + return; + } + // Defensive: no Commander-registered option parser in this CLI throws, and + // action handlers are already wrapped by handleCommand(). + if (error instanceof AuthenticationError) { + outputAuthError(error); + return; + } + outputError(error instanceof Error ? error : new Error(String(error))); +} diff --git a/src/common/errors.ts b/src/common/errors.ts index 0cd97433..10316901 100644 --- a/src/common/errors.ts +++ b/src/common/errors.ts @@ -37,6 +37,13 @@ export function requiresParameterError( export const AUTH_ERROR_CODE = 42; +/** + * Exit code for a malformed invocation (unknown command/option, wrong argument + * count). Distinct from 1 — an application error such as "entity not found" — + * so a caller can tell "I called the CLI wrong" from "the entity is missing". + */ +export const USAGE_ERROR_CODE = 2; + export class AuthenticationError extends Error { readonly details: string; diff --git a/src/common/output.ts b/src/common/output.ts index 40790b28..c283e412 100644 --- a/src/common/output.ts +++ b/src/common/output.ts @@ -1,8 +1,10 @@ import type { CommandOptions } from "./auth.js"; +import type { UsageErrorPayload } from "./cli-errors.js"; import { AUTH_ERROR_CODE, AuthenticationError, invalidParameterError, + USAGE_ERROR_CODE, } from "./errors.js"; import type { JsonSerializable } from "./json.js"; @@ -101,6 +103,15 @@ export function outputAuthError(error: AuthenticationError): void { process.exit(AUTH_ERROR_CODE); } +/** + * Emit a malformed-invocation envelope. Classification lives in + * `cli-errors.ts`; this layer only writes and exits. + */ +export function outputUsageError(payload: UsageErrorPayload): void { + console.error(JSON.stringify(payload, null, 2)); + process.exit(USAGE_ERROR_CODE); +} + export function parseLimit(value: string): number { const limit = parseInt(value, 10); if (Number.isNaN(limit) || limit < 1) { diff --git a/src/main.ts b/src/main.ts index 8cd5305c..0455281a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -28,6 +28,10 @@ import { PROJECTS_META, setupProjectsCommands } from "./commands/projects.js"; import { setupTeamsCommands, TEAMS_META } from "./commands/teams.js"; import { setupUsersCommands, USERS_META } from "./commands/users.js"; import { setupVersionCommands, VERSION_META } from "./commands/version.js"; +import { + handleParseFailure, + interceptParseErrors, +} from "./common/cli-errors.js"; import { getRootOpts } from "./common/context.js"; import { parseFieldsList, setOutputOptions } from "./common/output.js"; import { maybeNotifyUpdate } from "./common/update-notifier.js"; @@ -109,4 +113,7 @@ program } }); -program.parseAsync(); +// Must run after every command is registered so the walk sees the whole tree. +interceptParseErrors(program); + +program.parseAsync().catch(handleParseFailure); diff --git a/tests/unit/common/cli-errors.test.ts b/tests/unit/common/cli-errors.test.ts new file mode 100644 index 00000000..6d438f73 --- /dev/null +++ b/tests/unit/common/cli-errors.test.ts @@ -0,0 +1,339 @@ +// tests/unit/common/cli-errors.test.ts +import { Command, type CommanderError, InvalidArgumentError } from "commander"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + describeUsageError, + handleParseFailure, + interceptParseErrors, + type UsageErrorPayload, +} from "../../../src/common/cli-errors.js"; + +/** + * Mirrors the shape of the real tree in `src/main.ts`: a root with an action + * handler (the overview), domains whose action handler prints their own help + * (`issues.action(() => issues.help())`), a subcommand group with no action + * handler (`issues threads`, as built by `addCommentReactionCommands`), leaf + * commands with declared arguments, and a `usage` subcommand at both the root + * and domain level. + */ +function buildProgram() { + const program = new Command(); + program + .name("linearis") + .description("CLI for Linear.app with JSON output") + .version("1.2.3"); + program.action(() => console.log("overview")); + + const issues = program.command("issues").description("manage issues"); + issues.action(() => issues.help()); + + const threads = issues + .command("threads") + .description("discussion thread reaction operations"); + threads + .command("react") + .argument("", "thread identifier") + .action(() => {}); + + const read = issues + .command("read") + .description("read an issue") + .argument("", "issue identifier") + .action(() => {}); + + const list = issues + .command("list") + .description("list issues") + .option("--limit ", "max results") + .action(() => {}); + + issues + .command("usage") + .description("show issues usage") + .action(() => console.log("issues usage")); + + program + .command("usage") + .description("show overview") + .action(() => console.log("usage")); + + return { program, issues, threads, read, list }; +} + +/** + * Drive a parse failure and hand back the `CommanderError` raised by the + * command we expect to fail. Only `target` gets an exit override, so the + * assertion that `target` is the failing scope is part of the test rather than + * a reimplementation of the production tree walk. + */ +async function captureFailure( + program: Command, + target: Command, + argv: string[], +): Promise { + target.configureOutput({ writeErr: () => {} }); + let captured: CommanderError | undefined; + target.exitOverride((error) => { + captured = error; + throw error; + }); + await program.parseAsync(argv, { from: "user" }).catch(() => {}); + if (!captured) throw new Error(`expected ${target.name()} to fail parsing`); + return captured; +} + +describe("describeUsageError", () => { + it("reports an unknown domain against the root scope", async () => { + const { program } = buildProgram(); + const error = await captureFailure(program, program, [ + "issue", + "read", + "ABC-123", + ]); + + const payload = describeUsageError(error, program); + + expect(payload.error).toBe("UNKNOWN_COMMAND"); + expect(payload.message).toBe('Unknown command "issue" for "linearis".'); + expect(payload.command).toBe("linearis"); + expect(payload.available_commands).toEqual(["issues", "usage"]); + expect(payload.instruction).toBe( + "Run 'linearis usage' to list valid subcommands.", + ); + expect(payload.exit_code).toBe(2); + }); + + it("reports an unknown subcommand against the domain scope", async () => { + const { program, issues } = buildProgram(); + const error = await captureFailure(program, issues, [ + "issues", + "get", + "ABC-123", + ]); + + const payload = describeUsageError(error, issues); + + // Commander raises excessArguments (not unknownCommand) here because the + // domain has an action handler and no declared arguments. + expect(error.code).toBe("commander.excessArguments"); + expect(payload.error).toBe("UNKNOWN_COMMAND"); + expect(payload.message).toBe( + 'Unknown command "get" for "linearis issues".', + ); + expect(payload.command).toBe("linearis issues"); + expect(payload.available_commands).toEqual([ + "threads", + "read", + "list", + "usage", + ]); + expect(payload.instruction).toBe( + "Run 'linearis issues usage' to list valid subcommands.", + ); + }); + + it("reports excess arguments on a leaf command", async () => { + const { program, read } = buildProgram(); + const error = await captureFailure(program, read, [ + "issues", + "read", + "ABC-123", + "EXTRA", + ]); + + const payload = describeUsageError(error, read); + + expect(payload.error).toBe("TOO_MANY_ARGUMENTS"); + expect(payload.message).toContain("too many arguments for 'read'"); + expect(payload.message).not.toContain("error: "); + expect(payload.command).toBe("linearis issues read"); + // A leaf has no subcommands, so the key is omitted entirely. + expect(payload).not.toHaveProperty("available_commands"); + expect(payload.instruction).toBe( + "Run 'linearis issues usage' to see the valid arguments and options.", + ); + }); + + it("reports a missing required argument", async () => { + const { program, read } = buildProgram(); + const error = await captureFailure(program, read, ["issues", "read"]); + + const payload = describeUsageError(error, read); + + expect(payload.error).toBe("MISSING_ARGUMENT"); + expect(payload.message).toBe("missing required argument 'issue'"); + expect(payload.command).toBe("linearis issues read"); + }); + + it("reports an unknown option", async () => { + const { program, list } = buildProgram(); + const error = await captureFailure(program, list, [ + "issues", + "list", + "--bogus", + ]); + + const payload = describeUsageError(error, list); + + expect(payload.error).toBe("UNKNOWN_OPTION"); + expect(payload.message).toBe("unknown option '--bogus'"); + expect(payload.command).toBe("linearis issues list"); + }); + + it("maps a genuine commander.unknownCommand to UNKNOWN_COMMAND", async () => { + // A parent without an action handler routes unrecognised operands through + // unknownCommand() instead of _excessArguments(). + const program = new Command(); + program.name("linearis"); + program.command("issues").description("manage issues"); + program.command("usage").description("show overview"); + + const error = await captureFailure(program, program, ["bogus"]); + + expect(error.code).toBe("commander.unknownCommand"); + const payload = describeUsageError(error, program); + expect(payload.error).toBe("UNKNOWN_COMMAND"); + expect(payload.message).toBe('Unknown command "bogus" for "linearis".'); + }); + + it("names the scope when a subcommand group gets no subcommand", async () => { + const { program, threads } = buildProgram(); + const error = await captureFailure(program, threads, ["issues", "threads"]); + + const payload = describeUsageError(error, threads); + + // Commander asks the group to print its own help, with exit code 1 and the + // internal "(outputHelp)" placeholder as the message. + expect(error.code).toBe("commander.help"); + expect(error.exitCode).toBe(1); + expect(payload.error).toBe("INVALID_USAGE"); + expect(payload.message).toBe( + 'Missing subcommand for "linearis issues threads".', + ); + expect(payload.command).toBe("linearis issues threads"); + expect(payload.available_commands).toEqual(["react"]); + expect(payload.instruction).toBe( + "Run 'linearis issues usage' to list valid subcommands.", + ); + expect(payload.exit_code).toBe(2); + }); + + it("falls back to INVALID_USAGE for other Commander failures", async () => { + const { program, list } = buildProgram(); + list.option("--count ", "count", () => { + throw new InvalidArgumentError("must be a positive integer"); + }); + + const error = await captureFailure(program, list, [ + "issues", + "list", + "--count", + "x", + ]); + + const payload = describeUsageError(error, list); + + expect(payload.error).toBe("INVALID_USAGE"); + expect(payload.command).toBe("linearis issues list"); + expect(payload.exit_code).toBe(2); + }); +}); + +describe("interceptParseErrors + handleParseFailure", () => { + let stderrSpy: ReturnType; + let stdoutSpy: ReturnType; + let consoleErrorSpy: ReturnType; + let consoleLogSpy: ReturnType; + let exitSpy: ReturnType; + + beforeEach(() => { + stderrSpy = vi + .spyOn(process.stderr, "write") + .mockImplementation(() => true); + stdoutSpy = vi + .spyOn(process.stdout, "write") + .mockImplementation(() => true); + consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + exitSpy = vi + .spyOn(process, "exit") + .mockImplementation(() => undefined as never); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + async function runCli(argv: string[]): Promise { + const { program } = buildProgram(); + interceptParseErrors(program); + await program.parseAsync(argv, { from: "user" }).catch(handleParseFailure); + } + + function emittedPayload(): UsageErrorPayload { + const raw = consoleErrorSpy.mock.calls[0]?.[0] as string; + return JSON.parse(raw) as UsageErrorPayload; + } + + it("emits a JSON envelope on stderr and exits 2", async () => { + await runCli(["issues", "get", "ABC-123"]); + + expect(emittedPayload()).toMatchObject({ + error: "UNKNOWN_COMMAND", + command: "linearis issues", + exit_code: 2, + }); + expect(exitSpy).toHaveBeenCalledWith(2); + // Commander's plain-text message must not reach stderr alongside the JSON. + expect(stderrSpy).not.toHaveBeenCalled(); + }); + + it("emits an envelope instead of help text for a bare subcommand group", async () => { + await runCli(["issues", "threads"]); + + expect(emittedPayload()).toMatchObject({ + error: "INVALID_USAGE", + message: 'Missing subcommand for "linearis issues threads".', + command: "linearis issues threads", + exit_code: 2, + }); + expect(exitSpy).toHaveBeenCalledWith(2); + expect(stderrSpy).not.toHaveBeenCalled(); + }); + + it.each([ + ["--help"], + ["--version"], + ["issues"], + ])("leaves %s untouched and exits 0", async (arg) => { + await runCli([arg]); + + expect(consoleErrorSpy).not.toHaveBeenCalled(); + expect(stderrSpy).not.toHaveBeenCalled(); + expect(exitSpy).toHaveBeenCalledWith(0); + expect(exitSpy).not.toHaveBeenCalledWith(2); + // All three throw an exit-code-0 CommanderError after writing to stdout: + // `--help` and a bare domain via `help()`, `--version` via its own writer. + expect( + stdoutSpy.mock.calls.length + consoleLogSpy.mock.calls.length, + ).toBeGreaterThan(0); + }); + + it("routes a non-Commander rejection to outputError", () => { + handleParseFailure(new Error("boom")); + + expect(consoleErrorSpy).toHaveBeenCalledWith( + JSON.stringify({ error: "boom" }, null, 2), + ); + expect(exitSpy).toHaveBeenCalledWith(1); + }); + + it("wraps a non-Error rejection", () => { + handleParseFailure("plain string"); + + expect(consoleErrorSpy).toHaveBeenCalledWith( + JSON.stringify({ error: "plain string" }, null, 2), + ); + expect(exitSpy).toHaveBeenCalledWith(1); + }); +}); diff --git a/tests/unit/common/output.test.ts b/tests/unit/common/output.test.ts index 17c38a00..64f7436d 100644 --- a/tests/unit/common/output.test.ts +++ b/tests/unit/common/output.test.ts @@ -1,11 +1,13 @@ // tests/unit/common/output.test.ts import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { UsageErrorPayload } from "../../../src/common/cli-errors.js"; import { AuthenticationError } from "../../../src/common/errors.js"; import { handleCommand, outputAuthError, outputError, outputSuccess, + outputUsageError, parseFieldsList, parseLimit, pickFields, @@ -253,6 +255,31 @@ describe("handleCommand with AuthenticationError", () => { }); }); +describe("outputUsageError", () => { + it("writes the envelope to stderr and exits 2", () => { + const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + const exitSpy = vi + .spyOn(process, "exit") + .mockImplementation(() => undefined as never); + + const payload: UsageErrorPayload = { + error: "UNKNOWN_COMMAND", + message: 'Unknown command "get" for "linearis issues".', + command: "linearis issues", + available_commands: ["read", "usage"], + instruction: "Run 'linearis issues usage' to list valid subcommands.", + exit_code: 2, + }; + outputUsageError(payload); + + expect(consoleSpy).toHaveBeenCalledWith(JSON.stringify(payload, null, 2)); + expect(exitSpy).toHaveBeenCalledWith(2); + + consoleSpy.mockRestore(); + exitSpy.mockRestore(); + }); +}); + describe("parseLimit", () => { it("parses valid integer string", () => { expect(parseLimit("50")).toBe(50); From 23235c2032cfe2cec54d8d31f9b0eba8b9a17968 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:21:56 +0200 Subject: [PATCH 14/34] docs: document linearis exit codes and the usage-error envelope Nothing documented the exit codes, which made the newly distinct code 2 (and the existing 42) undiscoverable for the agents that are the main consumers of this CLI. Adds an "Exit codes" section to the README covering all four codes and the usage-error envelope, and extends the development docs "Output Format" block with outputUsageError, outputAuthError, and a pointer to cli-errors.ts. Refs #281 --- README.md | 28 ++++++++++++++++++++++++++++ docs/development.md | 22 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/README.md b/README.md index 5729977d..ec2e06e3 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,34 @@ The 🔴 rows are mostly workspace administration and integration plumbing — w > [!TIP] > Missing something you need day to day? [Open an issue](https://github.com/linearis-oss/linearis/issues) — the covered set is driven by what people actually reach for, not by API completeness. +## Exit codes + +Every failure is JSON on stderr, and the exit code says which class of failure it is: + +| Code | Meaning | Payload | +|---|---|---| +| `0` | Success | Result JSON on stdout | +| `1` | Application error — the request was well-formed but could not be fulfilled (entity not found, API rejection) | `{ "error": "" }` | +| `2` | Invalid invocation — unknown command or option, wrong number of arguments | Usage envelope (below) | +| `42` | Authentication required — no usable token, or the stored one is invalid | `{ "error": "AUTHENTICATION_REQUIRED", … }` | + +Exit code `2` carries a machine-readable recovery path: + +```json +{ + "error": "UNKNOWN_COMMAND", + "message": "Unknown command \"get\" for \"linearis issues\".", + "command": "linearis issues", + "available_commands": ["read", "list", "create", "…", "usage"], + "instruction": "Run 'linearis issues usage' to list valid subcommands.", + "exit_code": 2 +} +``` + +`error` is one of `UNKNOWN_COMMAND`, `UNKNOWN_OPTION`, `MISSING_ARGUMENT`, +`TOO_MANY_ARGUMENTS`, or `INVALID_USAGE`. `available_commands` is present only when +the failing scope has subcommands to choose from. + ## AI agent integration Linearis is structured around a **discover-then-act** pattern that matches how agents work: diff --git a/docs/development.md b/docs/development.md index 631c6c82..0d259763 100644 --- a/docs/development.md +++ b/docs/development.md @@ -280,8 +280,30 @@ outputSuccess(data); // JSON.stringify(data, null, 2) // Error: written to stderr, exits with code 1 outputError(error); // { "error": "message" } + +// Authentication error: written to stderr, exits with code 42 +outputAuthError(error); // { "error": "AUTHENTICATION_REQUIRED", ... } + +// Malformed invocation: written to stderr, exits with code 2 +outputUsageError(payload); // { "error": "UNKNOWN_COMMAND", ... } ``` +Exit codes: + +| Code | Constant | Meaning | +|---|---|---| +| `0` | — | Success | +| `1` | — | Application error (`outputError`) | +| `2` | `USAGE_ERROR_CODE` | Invalid invocation (`outputUsageError`) | +| `42` | `AUTH_ERROR_CODE` | Authentication required (`outputAuthError`) | + +`outputUsageError` is driven by `src/common/cli-errors.ts`, which classifies +Commander parse failures. `interceptParseErrors(program)` installs a per-command +`exitOverride` (capturing the failing `Command`, so the scope is exact) and +silences Commander's plain-text stderr writes; `program.parseAsync().catch(handleParseFailure)` +is the terminal handler. Both are wired at the bottom of `src/main.ts`, after +every command is registered. + ## Authentication For interactive setup, run `linearis auth login` — it opens Linear in the browser and stores the token encrypted in `~/.linearis/token`. From a4478075c73af9e0c0d3af699c68402559c05343 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:47:53 +0200 Subject: [PATCH 15/34] fix(cli): keep the usage-error message on a single line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With `showSuggestionAfterError` — Commander's default — a near miss is appended to the message as a parenthesised second line, and `describeUsageError` reused that string verbatim: $ linearis issues list --limt 5 { "message": "unknown option '--limt'\n(Did you mean --limit?)", ... } An embedded newline in a machine-readable field forces every consumer to know Commander's formatting to get either half back out, and the envelope's own `instruction` field already occupies the "here is what to do next" slot. Split the hint onto its own optional `suggestion` key, unwrapped from its parentheses, leaving `message` a single line. The split runs before the per-code message rewrite rather than after, so the hint survives for UNKNOWN_COMMAND too — an unrecognised subcommand is precisely the case Commander can suggest a near miss for, and that is where it helps most. --- README.md | 6 +++-- src/common/cli-errors.ts | 33 +++++++++++++++++++++++++++- tests/unit/common/cli-errors.test.ts | 20 +++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ec2e06e3..bb55d2b7 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,7 @@ Exit code `2` carries a machine-readable recovery path: { "error": "UNKNOWN_COMMAND", "message": "Unknown command \"get\" for \"linearis issues\".", + "suggestion": "Did you mean read?", "command": "linearis issues", "available_commands": ["read", "list", "create", "…", "usage"], "instruction": "Run 'linearis issues usage' to list valid subcommands.", @@ -172,8 +173,9 @@ Exit code `2` carries a machine-readable recovery path: ``` `error` is one of `UNKNOWN_COMMAND`, `UNKNOWN_OPTION`, `MISSING_ARGUMENT`, -`TOO_MANY_ARGUMENTS`, or `INVALID_USAGE`. `available_commands` is present only when -the failing scope has subcommands to choose from. +`TOO_MANY_ARGUMENTS`, or `INVALID_USAGE`. `message` is always a single line. +`suggestion` is present only when there is a close-enough near miss, and +`available_commands` only when the failing scope has subcommands to choose from. ## AI agent integration diff --git a/src/common/cli-errors.ts b/src/common/cli-errors.ts index 01c861ec..72c3d35c 100644 --- a/src/common/cli-errors.ts +++ b/src/common/cli-errors.ts @@ -11,7 +11,10 @@ type UsageErrorCode = export interface UsageErrorPayload { error: UsageErrorCode; + /** Single-line description of the failure. */ message: string; + /** Commander's near-miss hint, e.g. "Did you mean --limit?" — when it has one. */ + suggestion?: string; /** Space-joined path of the command that failed to parse, e.g. "linearis issues". */ command: string; /** Present only when the failing scope has subcommands to choose from. */ @@ -99,6 +102,28 @@ function offendingToken( : command.args[command.registeredArguments.length]; } +/** + * Split Commander's near-miss hint off the message. + * + * With `showSuggestionAfterError` (on by default) Commander appends the hint as + * a parenthesised second line — `unknown option '--limt'\n(Did you mean + * --limit?)`. Reusing that verbatim would put an embedded newline in a + * machine-readable field, so the first line stays `message` and the hint gets + * its own key, unwrapped from its parentheses. + */ +function splitSuggestion(raw: string): { + message: string; + suggestion?: string; +} { + const [first = "", ...rest] = raw.split("\n"); + const message = first.replace(/^error: /, ""); + const hint = rest + .join(" ") + .trim() + .replace(/^\((.*)\)$/, "$1"); + return hint.length > 0 ? { message, suggestion: hint } : { message }; +} + /** * Turn a Commander parse failure into the JSON envelope the CLI contract * promises. Pure — exported for tests. @@ -118,12 +143,17 @@ export function describeUsageError( // so it needs its own phrasing rather than the generic message fallback. const missingSubcommand = error.code === "commander.help"; + // The hint is worth keeping even where the message is rewritten: an unknown + // subcommand is exactly the case Commander can suggest a near miss for. + const { message: commanderMessage, suggestion } = splitSuggestion( + error.message, + ); const message = code === "UNKNOWN_COMMAND" && token !== undefined ? `Unknown command "${token}" for "${path}".` : missingSubcommand ? `Missing subcommand for "${path}".` - : error.message.replace(/^error: /, ""); + : commanderMessage; const instruction = code === "UNKNOWN_COMMAND" || missingSubcommand @@ -135,6 +165,7 @@ export function describeUsageError( return { error: code, message, + ...(suggestion !== undefined ? { suggestion } : {}), command: path, ...(available.length > 0 ? { available_commands: available } : {}), instruction, diff --git a/tests/unit/common/cli-errors.test.ts b/tests/unit/common/cli-errors.test.ts index 6d438f73..5340851c 100644 --- a/tests/unit/common/cli-errors.test.ts +++ b/tests/unit/common/cli-errors.test.ts @@ -178,6 +178,26 @@ describe("describeUsageError", () => { expect(payload.error).toBe("UNKNOWN_OPTION"); expect(payload.message).toBe("unknown option '--bogus'"); expect(payload.command).toBe("linearis issues list"); + expect(payload).not.toHaveProperty("suggestion"); + }); + + it("moves Commander's near-miss hint out of the message", async () => { + const { program, list } = buildProgram(); + const error = await captureFailure(program, list, [ + "issues", + "list", + "--limt", + "5", + ]); + + // Commander appends the hint to the message as a parenthesised second line. + expect(error.message).toContain("\n"); + + const payload = describeUsageError(error, list); + + expect(payload.message).toBe("unknown option '--limt'"); + expect(payload.message).not.toContain("\n"); + expect(payload.suggestion).toBe("Did you mean --limit?"); }); it("maps a genuine commander.unknownCommand to UNKNOWN_COMMAND", async () => { From b4c3a8b58690da398c702147b1c1446f00ab34ca Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:50:33 +0200 Subject: [PATCH 16/34] fix(cli): give every bare command group the same MISSING_SUBCOMMAND envelope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Naming a group without a subcommand had two contradictory contracts, decided by whether that group happened to be registered with `.action(() => group.help())`: $ linearis issues threads # exit 2, INVALID_USAGE envelope on stderr $ linearis issues relations # exit 0, Commander help text on stdout Same class of invocation, opposite results. The exit-0 half is the worse one for this CLI's primary consumer: an agent gets human help text on stdout where it expects JSON, and an exit code that says the call succeeded when nothing ran. It is also redundant — `linearis issues usage` is the machine-readable reference and `linearis issues --help` the human one, both still exit 0. So the fourteen `group.action(() => group.help())` registrations are dropped and every group converges on the envelope, which names the scope and lists `available_commands`. The root keeps its action handler: `linearis` alone prints the overview, which is a real result rather than an incomplete invocation. That leaves the root as the only command pairing an action handler with no declared arguments, so it is now the sole caller of the excessArguments to UNKNOWN_COMMAND remapping; domains reach `unknownCommand()` directly, which has the side benefit that Commander's near-miss hint fires for subcommands (`linearis issues lst` suggests "Did you mean list?"). The shared outcome also gets its own code rather than reusing INVALID_USAGE. The earlier reasoning for reuse was that the published enum should not grow, but this branch is what publishes it, so adding `MISSING_SUBCOMMAND` is free now and a breaking change later. It is a distinct condition: the invocation is not malformed, it is incomplete, and the recovery is to pick from `available_commands` rather than to fix a token. INVALID_USAGE stays as the genuine catch-all for unclassified Commander failures. --- README.md | 13 +++++--- src/commands/attachments.ts | 2 -- src/commands/auth.ts | 2 -- src/commands/comments.ts | 2 -- src/commands/cycles.ts | 2 -- src/commands/documents.ts | 2 -- src/commands/files.ts | 2 -- src/commands/initiatives/index.ts | 2 -- src/commands/initiatives/updates.ts | 2 -- src/commands/issues.ts | 4 --- src/commands/labels.ts | 2 -- src/commands/milestones.ts | 2 -- src/commands/projects.ts | 2 -- src/commands/teams.ts | 2 -- src/commands/users.ts | 2 -- src/common/cli-errors.ts | 14 +++++--- tests/unit/common/cli-errors.test.ts | 48 +++++++++++++++++----------- 17 files changed, 48 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index bb55d2b7..d942f897 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ Every failure is JSON on stderr, and the exit code says which class of failure i |---|---|---| | `0` | Success | Result JSON on stdout | | `1` | Application error — the request was well-formed but could not be fulfilled (entity not found, API rejection) | `{ "error": "" }` | -| `2` | Invalid invocation — unknown command or option, wrong number of arguments | Usage envelope (below) | +| `2` | Invalid invocation — unknown command or option, wrong number of arguments, or a command group named without a subcommand | Usage envelope (below) | | `42` | Authentication required — no usable token, or the stored one is invalid | `{ "error": "AUTHENTICATION_REQUIRED", … }` | Exit code `2` carries a machine-readable recovery path: @@ -173,9 +173,14 @@ Exit code `2` carries a machine-readable recovery path: ``` `error` is one of `UNKNOWN_COMMAND`, `UNKNOWN_OPTION`, `MISSING_ARGUMENT`, -`TOO_MANY_ARGUMENTS`, or `INVALID_USAGE`. `message` is always a single line. -`suggestion` is present only when there is a close-enough near miss, and -`available_commands` only when the failing scope has subcommands to choose from. +`TOO_MANY_ARGUMENTS`, `MISSING_SUBCOMMAND`, or `INVALID_USAGE`. `message` is always +a single line. `suggestion` is present only when there is a close-enough near miss, +and `available_commands` only when the failing scope has subcommands to choose from. + +Naming a command group without a subcommand (`linearis issues`, `linearis issues +threads`) is a `MISSING_SUBCOMMAND` failure, not a request for help — use +`linearis issues usage` for the machine-readable reference or `linearis issues +--help` for the human one. `linearis` on its own still prints the overview. ## AI agent integration diff --git a/src/commands/attachments.ts b/src/commands/attachments.ts index 90003f6d..14c9ab2e 100644 --- a/src/commands/attachments.ts +++ b/src/commands/attachments.ts @@ -71,8 +71,6 @@ export function setupAttachmentsCommands(program: Command): void { .command("attachments") .description("Attachment operations"); - attachments.action(() => attachments.help()); - attachments .command("list [issue]") .description("list attachments on an issue") diff --git a/src/commands/auth.ts b/src/commands/auth.ts index 8f5c6a90..329babfd 100644 --- a/src/commands/auth.ts +++ b/src/commands/auth.ts @@ -112,8 +112,6 @@ export function setupAuthCommands(program: Command): void { .command("auth") .description("Authenticate with Linear API"); - auth.action(() => auth.help()); - // Login bypasses handleCommand() — interactive UX with raw stdin and process.exit auth .command("login") diff --git a/src/commands/comments.ts b/src/commands/comments.ts index 3a233279..6715de6d 100644 --- a/src/commands/comments.ts +++ b/src/commands/comments.ts @@ -74,8 +74,6 @@ export function setupCommentsCommands(program: Command): void { "\nDEPRECATED: kept for compatibility. Prefer `issues discuss`, `issues discussions`, `issues replies`, `issues reply`, `issues edit-reply`, and `issues delete-reply`.\nCompatibility mode only supports replying by root thread ID (nested-reply targets are not supported).\nCompatibility edit/delete accept root thread IDs and reply IDs.", ); - comments.action(() => comments.help()); - comments .command("list ") .description( diff --git a/src/commands/cycles.ts b/src/commands/cycles.ts index 7efbd0f7..7a775285 100644 --- a/src/commands/cycles.ts +++ b/src/commands/cycles.ts @@ -45,8 +45,6 @@ export const CYCLES_META: DomainMeta = { export function setupCyclesCommands(program: Command): void { const cycles = program.command("cycles").description("Cycle operations"); - cycles.action(() => cycles.help()); - cycles .command("list") .description("list cycles") diff --git a/src/commands/documents.ts b/src/commands/documents.ts index e9392eba..55f8bd43 100644 --- a/src/commands/documents.ts +++ b/src/commands/documents.ts @@ -94,8 +94,6 @@ export function setupDocumentsCommands(program: Command): void { .command("documents") .description("Document operations (project-level documentation)"); - documents.action(() => documents.help()); - documents .command("list") .description("list documents") diff --git a/src/commands/files.ts b/src/commands/files.ts index 261f0bfa..168792e2 100644 --- a/src/commands/files.ts +++ b/src/commands/files.ts @@ -25,8 +25,6 @@ export function setupFilesCommands(program: Command): void { .command("files") .description("Upload and download files from Linear storage."); - files.action(() => files.help()); - files .command("download ") .description("download a file from Linear storage") diff --git a/src/commands/initiatives/index.ts b/src/commands/initiatives/index.ts index fd003bcc..1ddcf8fb 100644 --- a/src/commands/initiatives/index.ts +++ b/src/commands/initiatives/index.ts @@ -29,8 +29,6 @@ export function setupInitiativesCommands(program: Command): void { .command("initiatives") .description("Initiative operations"); - initiatives.action(() => initiatives.help()); - setupInitiativeEntityCommands(initiatives); setupInitiativeRelationCommands(initiatives); setupInitiativeProjectCommands(initiatives); diff --git a/src/commands/initiatives/updates.ts b/src/commands/initiatives/updates.ts index ddc4159c..96d16504 100644 --- a/src/commands/initiatives/updates.ts +++ b/src/commands/initiatives/updates.ts @@ -44,8 +44,6 @@ export function setupInitiativeUpdateCommands(initiatives: Command): void { .command("updates") .description("initiative update operations"); - updates.action(() => updates.help()); - updates .command("list") .description("list initiative updates") diff --git a/src/commands/issues.ts b/src/commands/issues.ts index f567365b..15ca9c4d 100644 --- a/src/commands/issues.ts +++ b/src/commands/issues.ts @@ -526,14 +526,10 @@ function addFilterOptions(cmd: ReturnType): typeof cmd { export function setupIssuesCommands(program: Command): void { const issues = program.command("issues").description("Issue operations"); - issues.action(() => issues.help()); - const relations = issues .command("relations") .description("Issue relation operations"); - relations.action(() => relations.help()); - relations .command("list ") .description("list relations for an issue") diff --git a/src/commands/labels.ts b/src/commands/labels.ts index 62f8b24d..a22e4bdf 100644 --- a/src/commands/labels.ts +++ b/src/commands/labels.ts @@ -169,8 +169,6 @@ export const LABELS_META: DomainMeta = { export function setupLabelsCommands(program: Command): void { const labels = program.command("labels").description("Label operations"); - labels.action(() => labels.help()); - labels .command("list") .description("list available labels") diff --git a/src/commands/milestones.ts b/src/commands/milestones.ts index 029b1f61..d5ec1449 100644 --- a/src/commands/milestones.ts +++ b/src/commands/milestones.ts @@ -61,8 +61,6 @@ export function setupMilestonesCommands(program: Command): void { .command("milestones") .description("Project milestone operations"); - milestones.action(() => milestones.help()); - // List milestones in a project milestones .command("list") diff --git a/src/commands/projects.ts b/src/commands/projects.ts index e9e9fc20..085f1309 100644 --- a/src/commands/projects.ts +++ b/src/commands/projects.ts @@ -253,8 +253,6 @@ export function setupProjectsCommands(program: Command): void { .command("projects") .description("Project operations"); - projects.action(() => projects.help()); - projects .command("list") .description("list projects") diff --git a/src/commands/teams.ts b/src/commands/teams.ts index f88fdaec..00e828d8 100644 --- a/src/commands/teams.ts +++ b/src/commands/teams.ts @@ -258,8 +258,6 @@ function addTeamSettingFlags(command: Command): Command { export function setupTeamsCommands(program: Command): void { const teams = program.command("teams").description("Team operations"); - teams.action(() => teams.help()); - teams .command("list") .description("list all teams") diff --git a/src/commands/users.ts b/src/commands/users.ts index 15cac95c..791e0e9b 100644 --- a/src/commands/users.ts +++ b/src/commands/users.ts @@ -29,8 +29,6 @@ export const USERS_META: DomainMeta = { export function setupUsersCommands(program: Command): void { const users = program.command("users").description("User operations"); - users.action(() => users.help()); - users .command("list") .description("list workspace members") diff --git a/src/common/cli-errors.ts b/src/common/cli-errors.ts index 72c3d35c..abc16bbf 100644 --- a/src/common/cli-errors.ts +++ b/src/common/cli-errors.ts @@ -7,6 +7,7 @@ type UsageErrorCode = | "UNKNOWN_OPTION" | "MISSING_ARGUMENT" | "TOO_MANY_ARGUMENTS" + | "MISSING_SUBCOMMAND" | "INVALID_USAGE"; export interface UsageErrorPayload { @@ -87,6 +88,10 @@ function classify(error: CommanderError, command: Command): UsageErrorCode { return "UNKNOWN_OPTION"; case "commander.missingArgument": return "MISSING_ARGUMENT"; + // Commander asks a command to print its own help when it has subcommands, + // no action handler and no operand to dispatch on (`linearis issues`). + case "commander.help": + return "MISSING_SUBCOMMAND"; default: return "INVALID_USAGE"; } @@ -137,11 +142,10 @@ export function describeUsageError( const usagePath = commandPath(usageScope(command)); const token = offendingToken(error, command); - // Commander raises `commander.help` with exit code 1 when a group has - // subcommands, no action handler and no operand to dispatch on (`linearis - // issues threads`). Its message is the internal "(outputHelp)" placeholder, - // so it needs its own phrasing rather than the generic message fallback. - const missingSubcommand = error.code === "commander.help"; + // The message Commander raises `commander.help` with is its internal + // "(outputHelp)" placeholder, so this code needs its own phrasing rather than + // the generic message fallback. + const missingSubcommand = code === "MISSING_SUBCOMMAND"; // The hint is worth keeping even where the message is rewritten: an unknown // subcommand is exactly the case Commander can suggest a near miss for. diff --git a/tests/unit/common/cli-errors.test.ts b/tests/unit/common/cli-errors.test.ts index 5340851c..621898dc 100644 --- a/tests/unit/common/cli-errors.test.ts +++ b/tests/unit/common/cli-errors.test.ts @@ -10,11 +10,9 @@ import { /** * Mirrors the shape of the real tree in `src/main.ts`: a root with an action - * handler (the overview), domains whose action handler prints their own help - * (`issues.action(() => issues.help())`), a subcommand group with no action - * handler (`issues threads`, as built by `addCommentReactionCommands`), leaf - * commands with declared arguments, and a `usage` subcommand at both the root - * and domain level. + * handler (the overview) and no declared arguments, subcommand groups with no + * action handler (`issues`, `issues threads`), leaf commands with declared + * arguments, and a `usage` subcommand at both the root and domain level. */ function buildProgram() { const program = new Command(); @@ -25,7 +23,6 @@ function buildProgram() { program.action(() => console.log("overview")); const issues = program.command("issues").description("manage issues"); - issues.action(() => issues.help()); const threads = issues .command("threads") @@ -93,6 +90,10 @@ describe("describeUsageError", () => { const payload = describeUsageError(error, program); + // The root is the one command that still pairs an action handler (the + // overview) with no declared arguments, so Commander routes the operand to + // _excessArguments() instead of unknownCommand(). + expect(error.code).toBe("commander.excessArguments"); expect(payload.error).toBe("UNKNOWN_COMMAND"); expect(payload.message).toBe('Unknown command "issue" for "linearis".'); expect(payload.command).toBe("linearis"); @@ -113,9 +114,7 @@ describe("describeUsageError", () => { const payload = describeUsageError(error, issues); - // Commander raises excessArguments (not unknownCommand) here because the - // domain has an action handler and no declared arguments. - expect(error.code).toBe("commander.excessArguments"); + expect(error.code).toBe("commander.unknownCommand"); expect(payload.error).toBe("UNKNOWN_COMMAND"); expect(payload.message).toBe( 'Unknown command "get" for "linearis issues".', @@ -226,7 +225,7 @@ describe("describeUsageError", () => { // internal "(outputHelp)" placeholder as the message. expect(error.code).toBe("commander.help"); expect(error.exitCode).toBe(1); - expect(payload.error).toBe("INVALID_USAGE"); + expect(payload.error).toBe("MISSING_SUBCOMMAND"); expect(payload.message).toBe( 'Missing subcommand for "linearis issues threads".', ); @@ -308,23 +307,29 @@ describe("interceptParseErrors + handleParseFailure", () => { expect(stderrSpy).not.toHaveBeenCalled(); }); - it("emits an envelope instead of help text for a bare subcommand group", async () => { - await runCli(["issues", "threads"]); + // Every group behaves the same way, whether or not it is a domain: a domain + // and a nested group are the same class of incomplete invocation. + it.each([ + [["issues"], "linearis issues"], + [["issues", "threads"], "linearis issues threads"], + ])("emits an envelope for the bare group %s", async (argv, path) => { + await runCli(argv); expect(emittedPayload()).toMatchObject({ - error: "INVALID_USAGE", - message: 'Missing subcommand for "linearis issues threads".', - command: "linearis issues threads", + error: "MISSING_SUBCOMMAND", + message: `Missing subcommand for "${path}".`, + command: path, exit_code: 2, }); expect(exitSpy).toHaveBeenCalledWith(2); + // Commander's help text must not reach stdout or stderr alongside the JSON. expect(stderrSpy).not.toHaveBeenCalled(); + expect(stdoutSpy).not.toHaveBeenCalled(); }); it.each([ ["--help"], ["--version"], - ["issues"], ])("leaves %s untouched and exits 0", async (arg) => { await runCli([arg]); @@ -332,13 +337,20 @@ describe("interceptParseErrors + handleParseFailure", () => { expect(stderrSpy).not.toHaveBeenCalled(); expect(exitSpy).toHaveBeenCalledWith(0); expect(exitSpy).not.toHaveBeenCalledWith(2); - // All three throw an exit-code-0 CommanderError after writing to stdout: - // `--help` and a bare domain via `help()`, `--version` via its own writer. + // Both throw an exit-code-0 CommanderError after writing to stdout. expect( stdoutSpy.mock.calls.length + consoleLogSpy.mock.calls.length, ).toBeGreaterThan(0); }); + it("still exits 0 for the root overview", async () => { + await runCli([]); + + expect(consoleErrorSpy).not.toHaveBeenCalled(); + expect(consoleLogSpy).toHaveBeenCalledWith("overview"); + expect(exitSpy).not.toHaveBeenCalledWith(2); + }); + it("routes a non-Commander rejection to outputError", () => { handleParseFailure(new Error("boom")); From 5c542188af84153c6b735b86e33c3a24a319da12 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:53:58 +0200 Subject: [PATCH 17/34] refactor(common): move UsageErrorPayload next to its exit code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `output.ts` imported the payload type from `cli-errors.ts`, which imports `outputUsageError` back from `output.ts`. The cycle is type-only today, so it erases at build time and nothing observes it — but it is one value import in `output.ts` away from becoming a real module cycle, and the next person to add one gets to discover that. `errors.ts` already owns `USAGE_ERROR_CODE`, the constant the payload's `exit_code` carries, and imports neither module. Moving the interface and its code union there leaves `cli-errors.ts` (classify) and `output.ts` (write) both depending on `errors.ts` and no longer on each other's types. `UsageErrorCode` stays private to its module, re-derived in `cli-errors.ts` as `UsageErrorPayload["error"]` so the classifier's return type cannot drift from the field it populates. --- src/common/cli-errors.ts | 28 ++++++---------------------- src/common/errors.ts | 27 +++++++++++++++++++++++++++ src/common/output.ts | 2 +- tests/unit/common/cli-errors.test.ts | 2 +- tests/unit/common/output.test.ts | 2 +- 5 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/common/cli-errors.ts b/src/common/cli-errors.ts index abc16bbf..03a8e4e0 100644 --- a/src/common/cli-errors.ts +++ b/src/common/cli-errors.ts @@ -1,28 +1,12 @@ import type { Command, CommanderError } from "commander"; -import { AuthenticationError, USAGE_ERROR_CODE } from "./errors.js"; +import { + AuthenticationError, + USAGE_ERROR_CODE, + type UsageErrorPayload, +} from "./errors.js"; import { outputAuthError, outputError, outputUsageError } from "./output.js"; -type UsageErrorCode = - | "UNKNOWN_COMMAND" - | "UNKNOWN_OPTION" - | "MISSING_ARGUMENT" - | "TOO_MANY_ARGUMENTS" - | "MISSING_SUBCOMMAND" - | "INVALID_USAGE"; - -export interface UsageErrorPayload { - error: UsageErrorCode; - /** Single-line description of the failure. */ - message: string; - /** Commander's near-miss hint, e.g. "Did you mean --limit?" — when it has one. */ - suggestion?: string; - /** Space-joined path of the command that failed to parse, e.g. "linearis issues". */ - command: string; - /** Present only when the failing scope has subcommands to choose from. */ - available_commands?: string[]; - instruction: string; - exit_code: number; -} +type UsageErrorCode = UsageErrorPayload["error"]; /** * Pairs a Commander parse failure with the `Command` that raised it. diff --git a/src/common/errors.ts b/src/common/errors.ts index 10316901..66d6dbfe 100644 --- a/src/common/errors.ts +++ b/src/common/errors.ts @@ -44,6 +44,33 @@ export const AUTH_ERROR_CODE = 42; */ export const USAGE_ERROR_CODE = 2; +type UsageErrorCode = + | "UNKNOWN_COMMAND" + | "UNKNOWN_OPTION" + | "MISSING_ARGUMENT" + | "TOO_MANY_ARGUMENTS" + | "MISSING_SUBCOMMAND" + | "INVALID_USAGE"; + +/** + * The exit-code-2 envelope. Classified in `cli-errors.ts` and written by + * `outputUsageError`; it lives here, next to the exit code it carries, so those + * two modules do not have to import each other. + */ +export interface UsageErrorPayload { + error: UsageErrorCode; + /** Single-line description of the failure. */ + message: string; + /** Commander's near-miss hint, e.g. "Did you mean --limit?" — when it has one. */ + suggestion?: string; + /** Space-joined path of the command that failed to parse, e.g. "linearis issues". */ + command: string; + /** Present only when the failing scope has subcommands to choose from. */ + available_commands?: string[]; + instruction: string; + exit_code: number; +} + export class AuthenticationError extends Error { readonly details: string; diff --git a/src/common/output.ts b/src/common/output.ts index c283e412..6dd060bf 100644 --- a/src/common/output.ts +++ b/src/common/output.ts @@ -1,10 +1,10 @@ import type { CommandOptions } from "./auth.js"; -import type { UsageErrorPayload } from "./cli-errors.js"; import { AUTH_ERROR_CODE, AuthenticationError, invalidParameterError, USAGE_ERROR_CODE, + type UsageErrorPayload, } from "./errors.js"; import type { JsonSerializable } from "./json.js"; diff --git a/tests/unit/common/cli-errors.test.ts b/tests/unit/common/cli-errors.test.ts index 621898dc..0a39d3c4 100644 --- a/tests/unit/common/cli-errors.test.ts +++ b/tests/unit/common/cli-errors.test.ts @@ -5,8 +5,8 @@ import { describeUsageError, handleParseFailure, interceptParseErrors, - type UsageErrorPayload, } from "../../../src/common/cli-errors.js"; +import type { UsageErrorPayload } from "../../../src/common/errors.js"; /** * Mirrors the shape of the real tree in `src/main.ts`: a root with an action diff --git a/tests/unit/common/output.test.ts b/tests/unit/common/output.test.ts index 64f7436d..03e8c362 100644 --- a/tests/unit/common/output.test.ts +++ b/tests/unit/common/output.test.ts @@ -1,6 +1,6 @@ // tests/unit/common/output.test.ts import { beforeEach, describe, expect, it, vi } from "vitest"; -import type { UsageErrorPayload } from "../../../src/common/cli-errors.js"; +import type { UsageErrorPayload } from "../../../src/common/errors.js"; import { AuthenticationError } from "../../../src/common/errors.js"; import { handleCommand, From 7ab176fae9d0e723dcea3bb1ad0ea94c42591f04 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:54:38 +0200 Subject: [PATCH 18/34] refactor(cli): drop the unreachable auth branch from handleParseFailure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `handleParseFailure` special-cased `AuthenticationError` to preserve exit code 42, but nothing can reach it. The handler only sees rejections from `program.parseAsync()`, and every action handler is wrapped by `handleCommand()`, which maps that error itself; `auth login` deliberately bypasses the wrapper and catches internally. The only other code that runs during parse is Commander's option parsers, none of which touch auth. It was the one branch of the function with no test — for the good reason that there is no way to exercise it through the public entry point. Testing it would mean calling `handleParseFailure` with an error the CLI cannot produce, which asserts the branch exists rather than that it works. Removed, with the fallthrough to `outputError` documented instead: anything unexpected still leaves as JSON, just with the generic exit code 1. --- src/common/cli-errors.ts | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/common/cli-errors.ts b/src/common/cli-errors.ts index 03a8e4e0..d4c99220 100644 --- a/src/common/cli-errors.ts +++ b/src/common/cli-errors.ts @@ -1,10 +1,6 @@ import type { Command, CommanderError } from "commander"; -import { - AuthenticationError, - USAGE_ERROR_CODE, - type UsageErrorPayload, -} from "./errors.js"; -import { outputAuthError, outputError, outputUsageError } from "./output.js"; +import { USAGE_ERROR_CODE, type UsageErrorPayload } from "./errors.js"; +import { outputError, outputUsageError } from "./output.js"; type UsageErrorCode = UsageErrorPayload["error"]; @@ -61,9 +57,9 @@ function classify(error: CommanderError, command: Command): UsageErrorCode { case "commander.unknownCommand": return "UNKNOWN_COMMAND"; case "commander.excessArguments": - // A domain command (`issues`) declares no arguments but has subcommands, - // and its overview action handler makes Commander treat an unrecognised - // subcommand as an excess operand rather than an unknown command. + // The root declares no arguments but has subcommands, and its overview + // action handler makes Commander treat an unrecognised subcommand as an + // excess operand rather than an unknown command. return command.commands.length > 0 && command.registeredArguments.length === 0 ? "UNKNOWN_COMMAND" @@ -188,11 +184,15 @@ export function interceptParseErrors(program: Command): void { * Terminal handler for anything escaping `program.parseAsync()`. Before this * existed the promise was neither awaited nor caught, so a rejection during * parsing surfaced as a raw Node unhandled-rejection stack trace. + * + * Anything that is not a parse failure falls through to `outputError`: action + * handlers do their own error mapping inside `handleCommand()`, so this only + * has to keep the JSON contract for the unexpected. */ export function handleParseFailure(error: unknown): void { if (error instanceof CliUsageError) { - // `--help`, `--version` and a bare `linearis ` throw with exit code - // 0 after writing to stdout: success, not a usage error. + // `--help` and `--version` throw with exit code 0 after writing to stdout: + // success, not a usage error. if (error.commanderError.exitCode === 0) { process.exit(0); return; @@ -200,11 +200,5 @@ export function handleParseFailure(error: unknown): void { outputUsageError(describeUsageError(error.commanderError, error.command)); return; } - // Defensive: no Commander-registered option parser in this CLI throws, and - // action handlers are already wrapped by handleCommand(). - if (error instanceof AuthenticationError) { - outputAuthError(error); - return; - } outputError(error instanceof Error ? error : new Error(String(error))); } From cca71ec64c86148dbaa27987933d6fae05541082 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:27:44 +0200 Subject: [PATCH 19/34] fix(common): point auth recovery at 'auth login', not the bare group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every "you need to authenticate" message told the caller to run `linearis auth`. That worked while the group had an action handler that printed its own help, but 35e690d dropped those handlers so a bare group is now a MISSING_SUBCOMMAND usage error. The exit-42 envelope's `instruction` therefore handed back a command that exits 2 — the recovery path dead-ended in a second error, which is worse for an agent following the envelope literally than for a human who would just read the help. All three sites now name `linearis auth login`, the command that actually runs the interactive flow, and the skill doc quotes the new wording so agents match on the same string. Refs #281 --- skills/linearis/SKILL.md | 4 ++-- src/common/auth.ts | 4 ++-- src/common/output.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/skills/linearis/SKILL.md b/skills/linearis/SKILL.md index f9ef4010..fe3a002f 100644 --- a/skills/linearis/SKILL.md +++ b/skills/linearis/SKILL.md @@ -24,8 +24,8 @@ Drive [Linear.app](https://linear.app) from the shell via the `linearis` CLI (JS - **Not installed** — if the shell reports command-not-found, tell the user linearis isn't installed and offer `npm install -g linearis`. As a no-install fallback, prefix commands with `npx linearis@latest` (adds cold-start latency and needs network per call — fallback, not default). Never silently `npm install -g`. - **Auth required** — any command may fail with this envelope on stderr and exit code 42: - `{ "error": "AUTHENTICATION_REQUIRED", "action": "USER_ACTION_REQUIRED", "instruction": "Run 'linearis auth' …", "exit_code": 42 }`. - Detect it by `exit_code === 42` / `error === "AUTHENTICATION_REQUIRED"` (not paraphrased text) and surface the CLI's own `instruction`. `linearis auth` is an interactive browser flow you cannot complete — hand it to the user. + `{ "error": "AUTHENTICATION_REQUIRED", "action": "USER_ACTION_REQUIRED", "instruction": "Run 'linearis auth login' …", "exit_code": 42 }`. + Detect it by `exit_code === 42` / `error === "AUTHENTICATION_REQUIRED"` (not paraphrased text) and surface the CLI's own `instruction`. `linearis auth login` is an interactive browser flow you cannot complete — hand it to the user. - **Updates (advisory, never blocking)** — optionally run `linearis version check` once → `{ current, latest, channel, updateAvailable }`. If `updateAvailable` is true, mention it and ask the user before `npm install -g linearis@latest`, honoring `channel` (don't move a `next` user to `latest`). npm can hang or rate-limit; on any timeout/error just proceed with the installed version. Read the plain installed version with `linearis version` (JSON), not `--version`. ## Discover, then act diff --git a/src/common/auth.ts b/src/common/auth.ts index 17cfb2b1..2ad6e666 100644 --- a/src/common/auth.ts +++ b/src/common/auth.ts @@ -38,7 +38,7 @@ export function resolveApiToken(options: CommandOptions): ResolvedToken { const legacyFile = path.join(os.homedir(), ".linear_api_token"); if (fs.existsSync(legacyFile)) { console.error( - "Warning: ~/.linear_api_token is deprecated. Run 'linearis auth' to migrate.", + "Warning: ~/.linear_api_token is deprecated. Run 'linearis auth login' to migrate.", ); return { token: fs.readFileSync(legacyFile, "utf8").trim(), @@ -47,7 +47,7 @@ export function resolveApiToken(options: CommandOptions): ResolvedToken { } throw new Error( - "No API token found. Run 'linearis auth' to set up authentication.", + "No API token found. Run 'linearis auth login' to set up authentication.", ); } diff --git a/src/common/output.ts b/src/common/output.ts index 6dd060bf..75ef9627 100644 --- a/src/common/output.ts +++ b/src/common/output.ts @@ -93,7 +93,7 @@ export function outputAuthError(error: AuthenticationError): void { details: error.details, action: "USER_ACTION_REQUIRED", instruction: - "Run 'linearis auth' to set up or refresh your authentication token.", + "Run 'linearis auth login' to set up or refresh your authentication token.", exit_code: AUTH_ERROR_CODE, }, null, From 19ec3955285f7fcad3d74e21b294604f59d54ccb Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:27:53 +0200 Subject: [PATCH 20/34] fix(cli): classify the two option-shaped parse failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `commander.missingMandatoryOptionValue` and `commander.optionMissingArgument` were not in `classify()`, so they fell through to INVALID_USAGE — the code reserved for failures the CLI could not identify. Nine commands declare a `requiredOption`, which makes "forgot a required flag" one of the most likely ways to call this CLI wrong; `linearis attachments create --url x` reported INVALID_USAGE where a caller has every right to expect a specific code. Both now get their own code. They are kept apart rather than merged because the recoveries differ: MISSING_REQUIRED_OPTION means a flag is absent, MISSING_OPTION_ARGUMENT means it was passed with nothing after it — an agent retrying automatically needs to know which. Commander's own message already names the offending flag in both cases, so no rewriting is needed. Timing matters more than the size of the change: the usage envelope is new on this branch and unreleased, so widening the code union costs nothing now and would be a breaking contract change once published. Refs #281 --- README.md | 3 ++- src/common/cli-errors.ts | 7 +++++ src/common/errors.ts | 2 ++ tests/unit/common/cli-errors.test.ts | 39 +++++++++++++++++++++++++++- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d942f897..d664ffee 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,8 @@ Exit code `2` carries a machine-readable recovery path: ``` `error` is one of `UNKNOWN_COMMAND`, `UNKNOWN_OPTION`, `MISSING_ARGUMENT`, -`TOO_MANY_ARGUMENTS`, `MISSING_SUBCOMMAND`, or `INVALID_USAGE`. `message` is always +`MISSING_REQUIRED_OPTION`, `MISSING_OPTION_ARGUMENT`, `TOO_MANY_ARGUMENTS`, +`MISSING_SUBCOMMAND`, or `INVALID_USAGE`. `message` is always a single line. `suggestion` is present only when there is a close-enough near miss, and `available_commands` only when the failing scope has subcommands to choose from. diff --git a/src/common/cli-errors.ts b/src/common/cli-errors.ts index d4c99220..a7d9a278 100644 --- a/src/common/cli-errors.ts +++ b/src/common/cli-errors.ts @@ -68,6 +68,13 @@ function classify(error: CommanderError, command: Command): UsageErrorCode { return "UNKNOWN_OPTION"; case "commander.missingArgument": return "MISSING_ARGUMENT"; + // A declared `requiredOption` was left out entirely (`--url` on + // `attachments create`); distinct from an option that was passed but got no + // value (`--url` with nothing after it). + case "commander.missingMandatoryOptionValue": + return "MISSING_REQUIRED_OPTION"; + case "commander.optionMissingArgument": + return "MISSING_OPTION_ARGUMENT"; // Commander asks a command to print its own help when it has subcommands, // no action handler and no operand to dispatch on (`linearis issues`). case "commander.help": diff --git a/src/common/errors.ts b/src/common/errors.ts index 66d6dbfe..11c97ff4 100644 --- a/src/common/errors.ts +++ b/src/common/errors.ts @@ -48,6 +48,8 @@ type UsageErrorCode = | "UNKNOWN_COMMAND" | "UNKNOWN_OPTION" | "MISSING_ARGUMENT" + | "MISSING_REQUIRED_OPTION" + | "MISSING_OPTION_ARGUMENT" | "TOO_MANY_ARGUMENTS" | "MISSING_SUBCOMMAND" | "INVALID_USAGE"; diff --git a/tests/unit/common/cli-errors.test.ts b/tests/unit/common/cli-errors.test.ts index 0a39d3c4..f3e92639 100644 --- a/tests/unit/common/cli-errors.test.ts +++ b/tests/unit/common/cli-errors.test.ts @@ -44,6 +44,12 @@ function buildProgram() { .option("--limit ", "max results") .action(() => {}); + const create = issues + .command("create") + .description("create an issue") + .requiredOption("--title ", "issue title") + .action(() => {}); + issues .command("usage") .description("show issues usage") @@ -54,7 +60,7 @@ function buildProgram() { .description("show overview") .action(() => console.log("usage")); - return { program, issues, threads, read, list }; + return { program, issues, threads, read, list, create }; } /** @@ -124,6 +130,7 @@ describe("describeUsageError", () => { "threads", "read", "list", + "create", "usage", ]); expect(payload.instruction).toBe( @@ -180,6 +187,36 @@ describe("describeUsageError", () => { expect(payload).not.toHaveProperty("suggestion"); }); + it("reports an omitted required option", async () => { + const { program, create } = buildProgram(); + const error = await captureFailure(program, create, ["issues", "create"]); + + const payload = describeUsageError(error, create); + + expect(error.code).toBe("commander.missingMandatoryOptionValue"); + expect(payload.error).toBe("MISSING_REQUIRED_OPTION"); + expect(payload.message).toBe( + "required option '--title <title>' not specified", + ); + expect(payload.command).toBe("linearis issues create"); + }); + + it("reports an option left without its value", async () => { + const { program, create } = buildProgram(); + const error = await captureFailure(program, create, [ + "issues", + "create", + "--title", + ]); + + const payload = describeUsageError(error, create); + + expect(error.code).toBe("commander.optionMissingArgument"); + expect(payload.error).toBe("MISSING_OPTION_ARGUMENT"); + expect(payload.message).toBe("option '--title <title>' argument missing"); + expect(payload.command).toBe("linearis issues create"); + }); + it("moves Commander's near-miss hint out of the message", async () => { const { program, list } = buildProgram(); const error = await captureFailure(program, list, [ From de405afd709abe27cac3fedf0e06165e7b0becc5 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:00:06 +0200 Subject: [PATCH 21/34] fix(cli): disable Commander's implicit help subcommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dropping the per-group `.action(() => group.help())` handlers in 35e690d made every command group match the condition under which Commander adds its implicit `help` subcommand: subcommands present, no action handler. So `linearis issues help` and `linearis issues threads help` started printing human-readable help on stdout and exiting 0 — the precise contract MISSING_SUBCOMMAND was introduced to remove, an agent getting help text where it expects JSON and a success code for a call that ran nothing. It was also inconsistent: `linearis help` and `linearis version help` still returned UNKNOWN_COMMAND, because the root and `version` kept their action handlers, and `help` never appeared in `available_commands`. Disable it in the `interceptParseErrors` walk, which already visits every command after registration and is where the rest of the JSON-contract enforcement lives. `help` is now an UNKNOWN_COMMAND like any other unrecognised subcommand. The `--help` option is separate and unaffected, so the human path is still one flag away. Refs #281 --- src/common/cli-errors.ts | 8 ++++++++ tests/unit/common/cli-errors.test.ts | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/common/cli-errors.ts b/src/common/cli-errors.ts index a7d9a278..c0345b40 100644 --- a/src/common/cli-errors.ts +++ b/src/common/cli-errors.ts @@ -173,6 +173,13 @@ export function describeUsageError( * suppressing `writeErr` is what keeps stderr JSON-only; the same text is still * available on `CommanderError.message`. * + * The walk also disables Commander's implicit `help` subcommand, which it adds + * to any command that has subcommands and no action handler — i.e. to every + * command group. Left on, `linearis issues help` would print human-readable + * help on stdout and exit 0, the exact contract `MISSING_SUBCOMMAND` exists to + * avoid; off, it is an `UNKNOWN_COMMAND` like any other unrecognised + * subcommand. `--help` is a separate option and is unaffected. + * * Must be called after every command has been registered, so the walk sees the * whole tree. */ @@ -182,6 +189,7 @@ export function interceptParseErrors(program: Command): void { throw new CliUsageError(err, cmd); }); cmd.configureOutput({ writeErr: () => {} }); + cmd.helpCommand(false); for (const child of cmd.commands) install(child); }; install(program); diff --git a/tests/unit/common/cli-errors.test.ts b/tests/unit/common/cli-errors.test.ts index f3e92639..6bcb4ca4 100644 --- a/tests/unit/common/cli-errors.test.ts +++ b/tests/unit/common/cli-errors.test.ts @@ -364,6 +364,27 @@ describe("interceptParseErrors + handleParseFailure", () => { expect(stdoutSpy).not.toHaveBeenCalled(); }); + // Commander adds an implicit `help` subcommand to every command that has + // subcommands and no action handler, which is exactly the shape of a command + // group. It must not become a stdout-and-exit-0 escape hatch out of the + // MISSING_SUBCOMMAND contract. + it.each([ + [["issues", "help"], "linearis issues"], + [["issues", "threads", "help"], "linearis issues threads"], + ])("treats %s as an unknown command, not implicit help", async (argv, path) => { + await runCli(argv); + + expect(emittedPayload()).toMatchObject({ + error: "UNKNOWN_COMMAND", + message: `Unknown command "help" for "${path}".`, + command: path, + exit_code: 2, + }); + expect(emittedPayload().available_commands).not.toContain("help"); + expect(exitSpy).toHaveBeenCalledWith(2); + expect(stdoutSpy).not.toHaveBeenCalled(); + }); + it.each([ ["--help"], ["--version"], From 2bff44ff745df8e186d4a46f92feaac65ac4740c Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:01:20 +0200 Subject: [PATCH 22/34] fix(usage): list nested group subcommands in domain usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `linearis issues threads` fails with MISSING_SUBCOMMAND and an instruction to run `linearis issues usage` — but that output named `threads` as a single line and never listed `react`, `unreact`, or `unreact-id`. The caller was sent to a reference that could not answer the question it was sent to answer, the same dead end 14ab072 fixed for the bare `auth` group. `issues relations`, `issues replies`, and `initiatives updates` had it too. Walk the command tree depth-first instead of one level, rendering each command under the path it is actually invoked by (`threads react <thread> [emoji]`) and keying its options section the same way (`threads react options:`), which also disambiguates the several `react`/`unreact` leaves that now coexist in one listing. Groups keep their own line as the heading their children hang off. The alternative was to reword the envelope's instruction to lean on `available_commands` instead. That treats the symptom: SKILL.md tells agents `usage` is authoritative and always current, so the fix belongs in the output that makes that claim. Refs #281 --- src/common/usage.ts | 41 ++++++++++++++++++++++++++------- tests/unit/common/usage.test.ts | 35 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/common/usage.ts b/src/common/usage.ts index eba67bc5..12b2e5fd 100644 --- a/src/common/usage.ts +++ b/src/common/usage.ts @@ -35,9 +35,9 @@ function extractLongFlag(flags: string): string { return longPart || flags; } -function formatCommandSignature(cmd: Command): string { +function formatCommandSignature(cmd: Command, path: string): string { const args = cmd.registeredArguments; - const parts: string[] = [cmd.name()]; + const parts: string[] = [path]; if (args.length > 0) { for (const arg of args) { @@ -50,6 +50,31 @@ function formatCommandSignature(cmd: Command): string { return parts.join(" "); } +/** + * Every runnable command under a domain, depth-first, each paired with the + * space-separated path it is invoked by (`threads react`). + * + * Domains nest one level deep (`issues threads`, `issues relations`, + * `initiatives updates`), and a bare nested group is a MISSING_SUBCOMMAND whose + * recovery instruction points here — so listing the group's own name and + * stopping would send the caller to an output that cannot answer the question + * it was sent to answer. The group keeps its line, as the heading its children + * hang off; the children are what make the reference complete. + */ +function collectCommands( + parent: Command, + prefix: string, +): { path: string; cmd: Command }[] { + const entries: { path: string; cmd: Command }[] = []; + for (const cmd of parent.commands) { + if (cmd.name() === "usage") continue; + const path = prefix.length > 0 ? `${prefix} ${cmd.name()}` : cmd.name(); + entries.push({ path, cmd }); + entries.push(...collectCommands(cmd, path)); + } + return entries; +} + export function formatDomainUsage(command: Command, meta: DomainMeta): string { const lines: string[] = []; @@ -58,12 +83,12 @@ export function formatDomainUsage(command: Command, meta: DomainMeta): string { lines.push(meta.context); lines.push(""); - const subcommands = command.commands.filter((c) => c.name() !== "usage"); + const subcommands = collectCommands(command, ""); lines.push("commands:"); - const subcommandEntries = subcommands.map((c) => ({ - sig: formatCommandSignature(c), - desc: c.description(), + const subcommandEntries = subcommands.map(({ path, cmd }) => ({ + sig: formatCommandSignature(cmd, path), + desc: cmd.description(), })); const maxSigLen = Math.max(...subcommandEntries.map((e) => e.sig.length)); @@ -83,12 +108,12 @@ export function formatDomainUsage(command: Command, meta: DomainMeta): string { } } - for (const cmd of subcommands) { + for (const { path, cmd } of subcommands) { const opts = cmd.options.filter((o) => !o.hidden); if (opts.length === 0) continue; lines.push(""); - lines.push(`${cmd.name()} options:`); + lines.push(`${path} options:`); const optionEntries = opts.map((o) => ({ flag: extractLongFlag(o.flags), diff --git a/tests/unit/common/usage.test.ts b/tests/unit/common/usage.test.ts index 513d68d8..a392d449 100644 --- a/tests/unit/common/usage.test.ts +++ b/tests/unit/common/usage.test.ts @@ -140,6 +140,41 @@ describe("formatDomainUsage", () => { ); }); + // A bare nested group (`linearis issues threads`) is a MISSING_SUBCOMMAND + // whose recovery instruction points at `linearis issues usage`, so that + // output has to name the nested subcommands and their options. + it("expands nested command groups under their invocation path", () => { + const domain = new Command("issues").description("Issue operations"); + const threads = domain + .command("threads") + .description("discussion thread reaction operations"); + threads + .command("react <thread> [emoji]") + .description("add a reaction to a discussion thread") + .option("--issue <issue>", "owning issue"); + threads + .command("unreact <thread>") + .description("remove your reaction from a discussion thread"); + + const meta: DomainMeta = { + name: "issues", + summary: "work items", + context: "an issue belongs to exactly one team.", + arguments: {}, + seeAlso: [], + }; + + const result = formatDomainUsage(domain, meta); + + // The group keeps its own line as the heading its children hang off. + expect(result).toMatch(/^ +threads +discussion thread reaction/m); + expect(result).toContain("threads react <thread> [emoji]"); + expect(result).toContain("threads unreact <thread>"); + // Options are keyed by the full path, not the ambiguous leaf name. + expect(result).toContain("threads react options:"); + expect(result).not.toContain("\nreact options:"); + }); + it("omits arguments and see-also sections when empty", () => { const domain = new Command("teams").description("Team operations"); domain.command("list").description("list all teams"); From 5756969194e8a0488d30148e09a8d441deaf6534 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:01:41 +0200 Subject: [PATCH 23/34] docs(skill): document the exit-code-2 usage-error envelope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The agent-facing skill taught only one machine-readable failure contract, exit 42 for authentication, even though this branch added a second one — exit 2 with a usage envelope carrying `available_commands` and an `instruction`. README and docs/development.md both describe it, but an agent driving the CLI reads SKILL.md, and without it a bare group or a mistyped flag looks like an ordinary failure to paraphrase rather than a structured recovery path to follow. Kept to the same reactive shape as the auth entry, branching on the envelope the CLI already emitted rather than pre-checking. Refs #281 --- skills/linearis/SKILL.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/skills/linearis/SKILL.md b/skills/linearis/SKILL.md index fe3a002f..b85d6c7e 100644 --- a/skills/linearis/SKILL.md +++ b/skills/linearis/SKILL.md @@ -26,6 +26,9 @@ Drive [Linear.app](https://linear.app) from the shell via the `linearis` CLI (JS - **Auth required** — any command may fail with this envelope on stderr and exit code 42: `{ "error": "AUTHENTICATION_REQUIRED", "action": "USER_ACTION_REQUIRED", "instruction": "Run 'linearis auth login' …", "exit_code": 42 }`. Detect it by `exit_code === 42` / `error === "AUTHENTICATION_REQUIRED"` (not paraphrased text) and surface the CLI's own `instruction`. `linearis auth login` is an interactive browser flow you cannot complete — hand it to the user. +- **Invalid invocation** — an unknown command or option, a wrong argument count, or a command group named without a subcommand fails on stderr with exit code `2`: + `{ "error": "UNKNOWN_COMMAND", "message": "…", "suggestion": "Did you mean read?", "command": "linearis issues", "available_commands": [...], "instruction": "Run 'linearis issues usage' …", "exit_code": 2 }`. + Recover from the envelope, not by guessing: pick from `available_commands`, or run the `instruction`. `error` is one of `UNKNOWN_COMMAND`, `UNKNOWN_OPTION`, `MISSING_ARGUMENT`, `MISSING_REQUIRED_OPTION`, `MISSING_OPTION_ARGUMENT`, `TOO_MANY_ARGUMENTS`, `MISSING_SUBCOMMAND`, `INVALID_USAGE`. A bare group (`linearis issues`) is a failure, not a request for help. - **Updates (advisory, never blocking)** — optionally run `linearis version check` once → `{ current, latest, channel, updateAvailable }`. If `updateAvailable` is true, mention it and ask the user before `npm install -g linearis@latest`, honoring `channel` (don't move a `next` user to `latest`). npm can hang or rate-limit; on any timeout/error just proceed with the installed version. Read the plain installed version with `linearis version` (JSON), not `--version`. ## Discover, then act From 8a723205534d0bb3c663a4911659ebdec848a26a Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:48:57 +0200 Subject: [PATCH 24/34] chore(plugin): bump the plugin to 1.1.0 for the exit-code-2 docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skill now documents the exit-code-2 usage-error envelope (UNKNOWN_COMMAND, MISSING_SUBCOMMAND, and the rest of the family) and corrects the auth recovery hint to 'linearis auth login'. Consumers pin and update the plugin by the version in the marketplace manifest, so a skill change that alters how an agent recovers from failures has to be published under a new version or existing installs never pick it up. Minor rather than patch: the skill gained a new documented behaviour contract, additively, with no instruction removed. The marketplace metadata version moves in lockstep with the single plugin it lists — keeping them equal is cheaper to reason about than tracking two independent counters for a one-plugin catalogue. Also extends the plugin description to mention error envelopes and exit codes, since that is now a substantive part of what the skill teaches and the description is what the marketplace listing shows. Refs #281 --- .claude-plugin/marketplace.json | 4 ++-- .claude-plugin/plugin.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d6279fc1..7b4c53d8 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,14 +6,14 @@ }, "metadata": { "description": "Marketplace for the linearis Claude Code plugin.", - "version": "1.0.0" + "version": "1.1.0" }, "plugins": [ { "name": "linearis", "source": "./", "description": "Agent skill teaching agents to use the linearis Linear.app CLI.", - "version": "1.0.0", + "version": "1.1.0", "author": { "name": "linearis-oss" }, "homepage": "https://github.com/linearis-oss/linearis", "license": "MIT" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 6f33d71f..72406ea3 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "linearis", - "description": "Agent skill for the linearis Linear.app CLI: preflight, discover-then-act usage protocol, JSON output, ID resolution, discussions, files.", - "version": "1.0.0", + "description": "Agent skill for the linearis Linear.app CLI: preflight, discover-then-act usage protocol, JSON output, error envelopes and exit codes, ID resolution, discussions, files.", + "version": "1.1.0", "author": { "name": "linearis-oss", "url": "https://github.com/linearis-oss/linearis" From 881ddf4a2083fb8deb2a25e33c8d677ebfd2e020 Mon Sep 17 00:00:00 2001 From: semantic-release-bot <semantic-release-bot@martynus.net> Date: Thu, 6 Aug 2026 10:51:46 +0000 Subject: [PATCH 25/34] chore(release): 2026.7.0-next.4 [skip ci] ## [2026.7.0-next.4](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.3...v2026.7.0-next.4) (2026-08-06) --- CHANGELOG.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b4e730e..2878da84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [2026.7.0-next.4](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.3...v2026.7.0-next.4) (2026-08-06) + ## [2026.7.0-next.3](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.2...v2026.7.0-next.3) (2026-08-06) ## [2026.7.0-next.2](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.1...v2026.7.0-next.2) (2026-07-06) diff --git a/package-lock.json b/package-lock.json index d9d49650..88a14c2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "linearis", - "version": "2026.7.0-next.3", + "version": "2026.7.0-next.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "linearis", - "version": "2026.7.0-next.3", + "version": "2026.7.0-next.4", "license": "MIT", "dependencies": { "commander": "15.0.0", diff --git a/package.json b/package.json index f54a0bc6..b419d6b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linearis", - "version": "2026.7.0-next.3", + "version": "2026.7.0-next.4", "description": "CLI tool for Linear.app with JSON output, smart ID resolution, and optimized GraphQL queries. Designed for LLM agents and humans who prefer structured data.", "main": "dist/main.js", "type": "module", From cba6234933927973cf760f4bade3bf0e9c47166f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:57:56 +0000 Subject: [PATCH 26/34] chore(deps): update dev dependencies (non-major) --- package-lock.json | 1330 ++++++++++++++++++++++----------------------- 1 file changed, 648 insertions(+), 682 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88a14c2d..f590aa13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,14 +96,14 @@ "license": "MIT" }, "node_modules/@ardatan/relay-compiler": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-13.0.1.tgz", - "integrity": "sha512-afG3YPwuSA0E5foouZusz5GlXKs74dObv4cuWyLyfKsYFj2r7oGRNB28v18HvwuLSQtQFCi+DpIe0TZkgQDYyg==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-13.0.2.tgz", + "integrity": "sha512-VFpv9UP820SiwDUPYtq7PmD3jifzZlevkQ26bhbSzFeruSTys0eHzQCZyKg+IhgmZzwPI9AFjPe26ABNjGeIKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.29.2", - "immutable": "^5.1.5", + "@babel/runtime": "^8.0.0", + "immutable": "^5.1.9", "invariant": "^2.2.4" }, "peerDependencies": { @@ -359,14 +359,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", - "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-8.0.0.tgz", + "integrity": "sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } + "license": "MIT" }, "node_modules/@babel/template": { "version": "7.28.6", @@ -427,9 +424,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.2.tgz", - "integrity": "sha512-VQ3RCqr7JmDIX+w6stWYl+g/3bYofN3q2wDBHUKKc/c7i5QWrFKFBZYCYPWTE6agsUPMIZZe6/CMmVUfUAhkKA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.6.tgz", + "integrity": "sha512-lxVNjv7UF6KfhMJfL9gaUHbWdJdHbsAj6OSmwSYNdhRuG67NxNQ4Xdvh3TUxsSK9sBzJBQhEJj3AopmmNJ5pSA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -443,20 +440,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.2", - "@biomejs/cli-darwin-x64": "2.5.2", - "@biomejs/cli-linux-arm64": "2.5.2", - "@biomejs/cli-linux-arm64-musl": "2.5.2", - "@biomejs/cli-linux-x64": "2.5.2", - "@biomejs/cli-linux-x64-musl": "2.5.2", - "@biomejs/cli-win32-arm64": "2.5.2", - "@biomejs/cli-win32-x64": "2.5.2" + "@biomejs/cli-darwin-arm64": "2.5.6", + "@biomejs/cli-darwin-x64": "2.5.6", + "@biomejs/cli-linux-arm64": "2.5.6", + "@biomejs/cli-linux-arm64-musl": "2.5.6", + "@biomejs/cli-linux-x64": "2.5.6", + "@biomejs/cli-linux-x64-musl": "2.5.6", + "@biomejs/cli-win32-arm64": "2.5.6", + "@biomejs/cli-win32-x64": "2.5.6" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.2.tgz", - "integrity": "sha512-e7P3P7EkwFc/KiX2AHw4YDLIBOMfG9CPCAwy52k5Bp0dfhkozx9hf6wCmIr2QeXy2XeccJ3V/Sg+hDmzYEqxSg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-zMOLZP4oMrjh6m1zcSj1ud2awUPgTuMVbmQhYYWL7J8HwCnbHHBvTm7VBTRuY7epT5bez76IpKYQ11ZAqHFlnw==", "cpu": [ "arm64" ], @@ -471,9 +468,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.2.tgz", - "integrity": "sha512-ymzMvjC1Jg0b9K0D26ZdARqFQXs7MocfLC5FOCGfkC0Ss+ACUJkX5364ZM5nT4NLZanHRZNVrZEy+Ibwcvux/g==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.6.tgz", + "integrity": "sha512-JAC1VqzvO7Th5ZplU0G2uGfkZbxEe9uDDektPAhF0JLusoz1w+T4okp2bkykI0bbaO2vslKiRfj4gU43JaGreA==", "cpu": [ "x64" ], @@ -488,9 +485,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.2.tgz", - "integrity": "sha512-t7sseOmqND57uUWTwlawU6BYj+J06T/9EkydzBhkrgw/FK3QVhjU2wsJR0frljrKZ0/I8A/rYw7284QgqjQfIQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.6.tgz", + "integrity": "sha512-6XsYwCFkp5sMxl85ffhgeGpGgs6A7dRYFnkceZ7WVxvycuTnGdD5xa534Z3xfrBQ0JCMK/mujT6ZNPJoghedwg==", "cpu": [ "arm64" ], @@ -508,9 +505,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.2.tgz", - "integrity": "sha512-w+ANG0ZvTu9IeEg9QnstoOnk6L0fpwJifW6aHR18+cb5Z39bkANItYjAfMrnvce5tmMK+IQ6nPX7/kQFdam5iw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-eUa3jeeYvfMt19LBeh6E5PUZpxnTC4JqNWo+EDjTtQjAr2xLGnWaxACtVU1DQqmHYbvThlJzLX+ZsYgrqh2qVw==", "cpu": [ "arm64" ], @@ -528,9 +525,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.2.tgz", - "integrity": "sha512-M/lOZrewzTCRDINbjhQ1gYYru37KlD3kJBQwwKCG0ckz5E9IZwIoJ3X0wBwRXA+yBDIwWUuPBHS67HzJY4dTfA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.6.tgz", + "integrity": "sha512-Pop9VXCFUhFTMfFefZ39S+u2rOPyNp5iHlxbZRwXGACHLy2r0jjiRgJHmaEKJzL3SyxlVeGShXhvvElvWowonA==", "cpu": [ "x64" ], @@ -548,9 +545,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.2.tgz", - "integrity": "sha512-VArNLAzND063tF+XY0yPyM+DyahpzOMzOAvb7qs259nhjJWRjvjZdssuA+Rfl+l07+NOesKZ0Xu2yFrXyBMtzw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-2Vp13QdKysH3HIWLaYLhUUwbK+jbZonJD1K+Lr0d0RO4wH7mkYd43vJixEDm8cUWrowoRz4UUHF1nm9Ae7ym8A==", "cpu": [ "x64" ], @@ -568,9 +565,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.2.tgz", - "integrity": "sha512-kbjFFKyZlzYnAuw7sRy5qDoFG6zrP40UK08oPQsWK0ct3NMnGSt+Bs1iviEEyEIP57N5MrykGXdO/wRiaR4lww==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.6.tgz", + "integrity": "sha512-tDGshcm6BdkZOCGnTDX0Y8/U4IfBSlnUU7T56nNDuPEfed+aHg+u8G36NB43fJVl0Os6+QURXIE1yuD7AaEofA==", "cpu": [ "arm64" ], @@ -585,9 +582,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.2.tgz", - "integrity": "sha512-4InchVpdVmdkkkgjQqKpgvyu+VPnoF/7RPSw5YATgEVpt2j72wcCAeV5TwaE9ZGJUZWZn7v2CwSAj6CrMJEx8A==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.6.tgz", + "integrity": "sha512-WN05KwXnTO/2J45RQPvzZMXf7tZUIofHoR35xIPfCo7pQ2RFidxI8sfb5mGsaTxdMmEOzHzOPRCdA5/fCpc7xQ==", "cpu": [ "x64" ], @@ -613,9 +610,9 @@ } }, "node_modules/@commitlint/cli": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.0.tgz", - "integrity": "sha512-4GLVIhUaT3c3GBlQ0GB80/5H3xXdn/Tgw4lrsuoOQVDu2wl4Xw0GuzSar8xZKSMv4H3xaKaQXmvH91GmdyYBZA==", + "version": "21.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.1.tgz", + "integrity": "sha512-blsZGe29hJ72VGEFVl72IVYX+1vsfINpjA9yWQA6i7OKD/McGEOXg08sKIRKjFk4JvzhV/9n0l3i6NooPLTNfg==", "dev": true, "license": "MIT", "dependencies": { @@ -623,7 +620,7 @@ "@commitlint/format": "^21.2.0", "@commitlint/lint": "^21.2.0", "@commitlint/load": "^21.2.0", - "@commitlint/read": "^21.2.0", + "@commitlint/read": "^21.2.1", "@commitlint/types": "^21.2.0", "tinyexec": "^1.0.0", "yargs": "^18.0.0" @@ -834,21 +831,61 @@ } }, "node_modules/@commitlint/read": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.0.tgz", - "integrity": "sha512-ELx8Ovh/JoAw5lpvDgxc6Y0We9skf2IPI2RFN+gnYgDGjRdMSF8zeodxhZmcclLWzfUIF7hXLOa8gOlllYcvBA==", + "version": "21.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.1.tgz", + "integrity": "sha512-hUW7EJQnNTL0vPOmVMNK4CrnrNBN0nN+JJHReFkdHO5y4iyHeEmTBwuC15OCqUTjxWo7idnH1LftfpWVIaPWIA==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/top-level": "^21.2.0", "@commitlint/types": "^21.2.0", - "git-raw-commits": "^5.0.0", + "@conventional-changelog/git-client": "^3.0.0", "tinyexec": "^1.0.0" }, "engines": { "node": ">=22.12.0" } }, + "node_modules/@commitlint/read/node_modules/@conventional-changelog/git-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-3.1.0.tgz", + "integrity": "sha512-Tqa/gHco2WJWa740NRjOrfKVvzIqxkZpecb8bemaQ8sKM5PXb1UK4uTyTb/1wIqNuOVaDOFxyBdhTIQZn6gdjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/child-process-utils": "^2.0.0", + "@simple-libs/stream-utils": "^2.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "conventional-commits-filter": "^6.0.1", + "conventional-commits-parser": "^7.0.1" + }, + "peerDependenciesMeta": { + "conventional-commits-filter": { + "optional": true + }, + "conventional-commits-parser": { + "optional": true + } + } + }, + "node_modules/@commitlint/read/node_modules/@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, "node_modules/@commitlint/resolve-extends": { "version": "21.2.0", "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.0.tgz", @@ -962,33 +999,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@conventional-changelog/git-client": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.7.0.tgz", - "integrity": "sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@simple-libs/child-process-utils": "^1.0.0", - "@simple-libs/stream-utils": "^1.2.0", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "conventional-commits-filter": "^5.0.0", - "conventional-commits-parser": "^6.4.0" - }, - "peerDependenciesMeta": { - "conventional-commits-filter": { - "optional": true - }, - "conventional-commits-parser": { - "optional": true - } - } - }, "node_modules/@conventional-changelog/template": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.2.0.tgz", @@ -1000,9 +1010,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", "dev": true, "license": "MIT", "optional": true, @@ -1012,9 +1022,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "dev": true, "license": "MIT", "optional": true, @@ -1527,35 +1537,35 @@ "license": "MIT" }, "node_modules/@graphql-codegen/add": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-7.0.1.tgz", - "integrity": "sha512-kWw6RMu9ysBw1wcgcgf9mOnswc5M3ekOApDTiaJC/UZNTEYins01srZHYTP7z3P/WlGGC844BRtjwh3U2kNd/A==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-7.1.0.tgz", + "integrity": "sha512-bytJg1kel5zfgK3JSYbGwtpbNe6F9OPZSR6DiMDe9RVxblAgl6w4zEEPd/mM3rhNJ1VmGYLbNnf5e1eUfXQEbg==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^7.0.1", + "@graphql-codegen/plugin-helpers": "^7.1.0", "tslib": "^2.8.0" }, "engines": { "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-codegen/cli": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-7.1.3.tgz", - "integrity": "sha512-mMYwpvpqJjjHoA/c6HBjdlbT8JqFC6W85RB80tpHACapufBnLlyNtYHYeOYAoUuU1n3cGQi1if1pKHnjLgS/eQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-7.2.0.tgz", + "integrity": "sha512-JPJw2vquEIpO3b8XJyxFVTrYi6WRn/OKu/SlzQA+IwAVT7GZPeG+AHmfRXAvpVMj31899nTpQYEQGUxx3ZqubQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/generator": "^7.18.13", "@babel/template": "^7.18.10", "@babel/types": "^7.18.13", - "@graphql-codegen/client-preset": "^6.0.1", - "@graphql-codegen/core": "^6.1.0", - "@graphql-codegen/plugin-helpers": "^7.0.1", + "@graphql-codegen/client-preset": "^6.1.0", + "@graphql-codegen/core": "^6.2.0", + "@graphql-codegen/plugin-helpers": "^7.1.0", "@graphql-tools/apollo-engine-loader": "^8.0.28", "@graphql-tools/code-file-loader": "^8.1.28", "@graphql-tools/git-loader": "^8.0.32", @@ -1565,7 +1575,7 @@ "@graphql-tools/load": "^8.1.8", "@graphql-tools/merge": "^9.0.6", "@graphql-tools/url-loader": "^9.0.6", - "@graphql-tools/utils": "^11.0.0", + "@graphql-tools/utils": "^11.2.0", "@inquirer/prompts": "^8.3.2", "@whatwg-node/fetch": "^0.10.0", "chalk": "^5.6.0", @@ -1598,7 +1608,7 @@ }, "peerDependencies": { "@parcel/watcher": "^2.1.0", - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" }, "peerDependenciesMeta": { "@parcel/watcher": { @@ -1620,23 +1630,23 @@ } }, "node_modules/@graphql-codegen/client-preset": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-6.0.1.tgz", - "integrity": "sha512-6wh0ZHG9WzBD6bE4AVOO6VCCMXK2orxHuXxaNKj+sj1w0qZ3Y3WIjZnqZLg6JZrHCIs/e+gy3T15Dc2pH8IbHA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-6.1.0.tgz", + "integrity": "sha512-mGmBuwrOU5oRoaWFodx8g9xu1jecYIiydqvk88QsAIsyMcZwuoybs1lyne85TovpBHjH5CC2wnZGsbDQfcgOCQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/template": "^7.20.7", - "@graphql-codegen/add": "^7.0.1", - "@graphql-codegen/gql-tag-operations": "^6.0.1", - "@graphql-codegen/plugin-helpers": "^7.0.1", - "@graphql-codegen/typed-document-node": "^7.0.1", - "@graphql-codegen/typescript": "^6.0.2", - "@graphql-codegen/typescript-operations": "^6.0.3", - "@graphql-codegen/visitor-plugin-common": "^7.0.3", + "@graphql-codegen/add": "^7.1.0", + "@graphql-codegen/gql-tag-operations": "^6.1.0", + "@graphql-codegen/plugin-helpers": "^7.1.0", + "@graphql-codegen/typed-document-node": "^7.1.0", + "@graphql-codegen/typescript": "^6.1.0", + "@graphql-codegen/typescript-operations": "^6.1.0", + "@graphql-codegen/visitor-plugin-common": "^7.2.0", "@graphql-tools/documents": "^1.0.0", - "@graphql-tools/utils": "^11.0.0", + "@graphql-tools/utils": "^11.2.0", "@graphql-typed-document-node/core": "3.2.0", "tslib": "^2.8.0" }, @@ -1644,7 +1654,7 @@ "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "graphql-sock": "^1.0.0" }, "peerDependenciesMeta": { @@ -1653,55 +1663,35 @@ } } }, - "node_modules/@graphql-codegen/client-preset/node_modules/@graphql-codegen/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-zyLfKsFJ7TRkQ0PyaUVuiAek9TSbtVJwwBoOuaE9RAWr45+9Y5W1LYldpiSTcyfxKVSIniE7Gj0V87qzrpdyYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^7.0.1", - "@graphql-codegen/schema-ast": "^6.0.1", - "@graphql-codegen/visitor-plugin-common": "^7.0.3", - "auto-bind": "^5.0.0", - "tslib": "~2.8.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, "node_modules/@graphql-codegen/core": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-6.1.0.tgz", - "integrity": "sha512-jReAzuCYlrSBJHW2bfBpDl/vMRCw0yQEoTvGi9K+3OTsazDXEQGOpCVfj8p/xO2h7ynu5Yrvzo0sUylVv0CnwA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-6.2.0.tgz", + "integrity": "sha512-RZadhhwYhuy2ZdIGK40vYVBMzXEFGkCC+58MUC/F2af/gKznEYNzHgmNBUBCk/BTklyUsNu0mIXmyGE4tTA0PA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^7.0.1", + "@graphql-codegen/plugin-helpers": "^7.1.0", "@graphql-tools/schema": "^10.0.0", - "@graphql-tools/utils": "^11.0.0", + "@graphql-tools/utils": "^11.2.0", "tslib": "^2.8.0" }, "engines": { "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-codegen/gql-tag-operations": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-6.0.1.tgz", - "integrity": "sha512-eHYUIchZLG6G+kafeKnUByL2Nkmb8Uj2vg33UVLFj8XJ2coC4b1iRDWxCdTXbupZrN0FaM0QRRBLs3zBEAzcJg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-6.1.0.tgz", + "integrity": "sha512-AmMcZFwonufvWJnQm7I0lBxKpAm+35BcCrOOvUlBoviohiR17aPoTGAOaNAEtpcpI86lnZ9m9AXUdiKMdm8nnQ==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^7.0.1", - "@graphql-codegen/visitor-plugin-common": "^7.0.3", - "@graphql-tools/utils": "^11.0.0", + "@graphql-codegen/plugin-helpers": "^7.1.0", + "@graphql-codegen/visitor-plugin-common": "^7.2.0", + "@graphql-tools/utils": "^11.2.0", "auto-bind": "^5.0.0", "tslib": "^2.8.0" }, @@ -1709,17 +1699,17 @@ "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-codegen/plugin-helpers": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-7.0.1.tgz", - "integrity": "sha512-S2X0YT3XQbP2haqhIeku8GOXo2j8QuBu7BrLsOEHz4UeMu78y3rja1Q4ri3oJ0jq4dMgaQlazoVHI/A+FAKMGw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-7.1.0.tgz", + "integrity": "sha512-ieJH7kZ5oSZKBPJs7CvHMrFY/CLYLklqv74ir93qMwRna6geZsbIMoJzTDBXohxcQTITiProiYSGrEtZjIpYGg==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^11.0.0", + "@graphql-tools/utils": "^11.2.0", "change-case-all": "^2.1.0", "common-tags": "1.8.2", "import-from": "4.0.0", @@ -1729,36 +1719,36 @@ "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-codegen/schema-ast": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-6.0.1.tgz", - "integrity": "sha512-P16b6XCWXfcrA4fkuAyqoy883USAULifv8YWgEOrNKDAnr2DR+Kr85jSomknIUTY39wiuvisv4/lrdXobwK6sA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-6.1.0.tgz", + "integrity": "sha512-/xuGkM5gUNFRoaQLumKbENdX7Hc8ha49z9OXsEZY8E+46mMjqzXGF0NtCJ892cmoX7EUgI5c8T+LZqS2upx2Aw==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^7.0.1", - "@graphql-tools/utils": "^11.0.0", + "@graphql-codegen/plugin-helpers": "^7.1.0", + "@graphql-tools/utils": "^11.2.0", "tslib": "^2.8.0" }, "engines": { "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-codegen/typed-document-node": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-7.0.3.tgz", - "integrity": "sha512-l/4KenYJG5D8Aj6Aa2KPeS0fdMIIi04Qx28d4SLwMWuyFU9WXspU5mR9YMNDRZzaTgBtGR8aMIl9RzyiWf5uUw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-7.1.0.tgz", + "integrity": "sha512-V6H+ItyqXtYY+JQb76LAoN627Xfzpn29/ifwCFAv61iEepzNzh86sa+yZclflr0G8LDmhcVY5hpPJd3a1qbOfw==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^7.0.1", - "@graphql-codegen/visitor-plugin-common": "^7.1.0", + "@graphql-codegen/plugin-helpers": "^7.1.0", + "@graphql-codegen/visitor-plugin-common": "^7.2.0", "auto-bind": "^5.0.0", "change-case-all": "^2.1.0", "tslib": "^2.8.0" @@ -1767,19 +1757,39 @@ "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-codegen/typescript": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-6.1.0.tgz", + "integrity": "sha512-2Hu3111O/AwV28Ap7tNsixlmXSAJuQbQArQklx+IC/tNswpckZnCfmlcBtTJrGU1+mJXEneJXGfb2XWvKjbhlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@graphql-codegen/plugin-helpers": "^7.1.0", + "@graphql-codegen/schema-ast": "^6.1.0", + "@graphql-codegen/visitor-plugin-common": "^7.2.0", + "auto-bind": "^5.0.0", + "tslib": "~2.8.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-codegen/typescript-operations": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-6.0.5.tgz", - "integrity": "sha512-amsfjYbLIbYUIPr481hlXXjgnm5knel3v6EXJ8oThhcbOoT332XJjzjohZaTjij7eqebOewzzRXLPWi8z+mgSA==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-6.1.2.tgz", + "integrity": "sha512-EP9xry09q4cOVaf/aC4NO3/SwvXRNzlJIe4dhfA0xyy45Taix5yDL3jeJpmJIv03sa7nK5udVs5vZqdHFU8Xmw==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^7.0.1", - "@graphql-codegen/schema-ast": "^6.0.1", - "@graphql-codegen/visitor-plugin-common": "^7.1.2", + "@graphql-codegen/plugin-helpers": "^7.1.0", + "@graphql-codegen/schema-ast": "^6.1.0", + "@graphql-codegen/visitor-plugin-common": "^7.2.2", "auto-bind": "^5.0.0", "tslib": "^2.8.0" }, @@ -1787,7 +1797,7 @@ "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "graphql-sock": "^1.0.0" }, "peerDependenciesMeta": { @@ -1797,16 +1807,16 @@ } }, "node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-7.1.2.tgz", - "integrity": "sha512-1TdaKeDBj91ZYcJO9fMHHo02HEoWCSYhlsi4B7EGto7cqQa2htraAi+sCYIxQuccxNsRmM6apQ+O5+cJj1Bs7w==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-7.2.2.tgz", + "integrity": "sha512-nOkAVd8J8r2YdHm9Z4YrBiy+3IQgE8Ndn/EiRWTvWuemMEhioBWyvdlnbq5rHmIo2bNdRQ5ghs0Q3jw7YBrwLQ==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^7.0.1", + "@graphql-codegen/plugin-helpers": "^7.1.0", "@graphql-tools/optimize": "^2.0.0", "@graphql-tools/relay-operation-optimizer": "^7.1.1", - "@graphql-tools/utils": "^11.0.0", + "@graphql-tools/utils": "^11.2.0", "auto-bind": "^5.0.0", "change-case-all": "^2.1.0", "dependency-graph": "^1.0.0", @@ -1818,7 +1828,7 @@ "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-hive/signal": { @@ -2208,14 +2218,14 @@ } }, "node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.1.5.tgz", - "integrity": "sha512-B3nscUeWT3wYucrvbJcmU8sAVlkCp+WhZ5wVlK432AfnLjDYUNLobHYzQnnU7tT5NncMXusPiS8K4YG4iUPjrw==", + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.1.8.tgz", + "integrity": "sha512-s16NYT+66VSLCITBURoGNTwh+YvZRSlW3MtFJC2gsvrHZHf6KpCvIR3Qju4yh0FtCoN7Wg7yOI/JT/UzaMEOwQ==", "dev": true, "license": "MIT", "dependencies": { - "@ardatan/relay-compiler": "^13.0.1", - "@graphql-tools/utils": "^11.1.1", + "@ardatan/relay-compiler": "^13.0.2", + "@graphql-tools/utils": "^11.2.2", "tslib": "^2.4.0" }, "engines": { @@ -2271,9 +2281,9 @@ } }, "node_modules/@graphql-tools/utils": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.1.1.tgz", - "integrity": "sha512-MuWwacINZZV6mX1ZSk6CcV4XVQLsbKBG/hLd0QPhe4GHxjqr2ATjKsnnwlB0TKI+QQvj2U8ewu8WeAzz9kC2Xg==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.2.2.tgz", + "integrity": "sha512-Do2A/t6ayqOma8m7PvpYMsCBswL+NB35BQSng4GWSBqafL2/BnfAZy/NSENPwV721Rm2fG0BHETFC0+FJJZmLw==", "dev": true, "license": "MIT", "dependencies": { @@ -2714,22 +2724,25 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", - "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" } }, "node_modules/@nodelib/fs.scandir": { @@ -2928,9 +2941,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", - "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.142.0.tgz", + "integrity": "sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==", "cpu": [ "arm" ], @@ -2945,9 +2958,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", - "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.142.0.tgz", + "integrity": "sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==", "cpu": [ "arm64" ], @@ -2962,9 +2975,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", - "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.142.0.tgz", + "integrity": "sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==", "cpu": [ "arm64" ], @@ -2979,9 +2992,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", - "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.142.0.tgz", + "integrity": "sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==", "cpu": [ "x64" ], @@ -2996,9 +3009,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", - "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.142.0.tgz", + "integrity": "sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==", "cpu": [ "x64" ], @@ -3013,9 +3026,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", - "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.142.0.tgz", + "integrity": "sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==", "cpu": [ "arm" ], @@ -3030,9 +3043,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", - "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.142.0.tgz", + "integrity": "sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==", "cpu": [ "arm" ], @@ -3047,9 +3060,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", - "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.142.0.tgz", + "integrity": "sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==", "cpu": [ "arm64" ], @@ -3067,9 +3080,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", - "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.142.0.tgz", + "integrity": "sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==", "cpu": [ "arm64" ], @@ -3087,9 +3100,9 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", - "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.142.0.tgz", + "integrity": "sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==", "cpu": [ "ppc64" ], @@ -3107,9 +3120,9 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", - "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.142.0.tgz", + "integrity": "sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==", "cpu": [ "riscv64" ], @@ -3127,9 +3140,9 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", - "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.142.0.tgz", + "integrity": "sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==", "cpu": [ "riscv64" ], @@ -3147,9 +3160,9 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", - "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.142.0.tgz", + "integrity": "sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==", "cpu": [ "s390x" ], @@ -3167,9 +3180,9 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", - "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.142.0.tgz", + "integrity": "sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==", "cpu": [ "x64" ], @@ -3187,9 +3200,9 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", - "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.142.0.tgz", + "integrity": "sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==", "cpu": [ "x64" ], @@ -3207,9 +3220,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", - "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.142.0.tgz", + "integrity": "sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==", "cpu": [ "arm64" ], @@ -3224,9 +3237,9 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", - "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.142.0.tgz", + "integrity": "sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==", "cpu": [ "wasm32" ], @@ -3234,18 +3247,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.5" + "@emnapi/core": "1.11.2", + "@emnapi/runtime": "1.11.2", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", - "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.142.0.tgz", + "integrity": "sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==", "cpu": [ "arm64" ], @@ -3260,9 +3273,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", - "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.142.0.tgz", + "integrity": "sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==", "cpu": [ "ia32" ], @@ -3277,9 +3290,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", - "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.142.0.tgz", + "integrity": "sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==", "cpu": [ "x64" ], @@ -3294,9 +3307,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", - "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", "dev": true, "license": "MIT", "funding": { @@ -3304,9 +3317,9 @@ } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", - "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.24.2.tgz", + "integrity": "sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==", "cpu": [ "arm" ], @@ -3318,9 +3331,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", - "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.24.2.tgz", + "integrity": "sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==", "cpu": [ "arm64" ], @@ -3332,9 +3345,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", - "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.24.2.tgz", + "integrity": "sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==", "cpu": [ "arm64" ], @@ -3346,9 +3359,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", - "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.24.2.tgz", + "integrity": "sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==", "cpu": [ "x64" ], @@ -3360,9 +3373,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", - "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.24.2.tgz", + "integrity": "sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==", "cpu": [ "x64" ], @@ -3374,9 +3387,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", - "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.24.2.tgz", + "integrity": "sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==", "cpu": [ "arm" ], @@ -3388,9 +3401,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", - "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.24.2.tgz", + "integrity": "sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==", "cpu": [ "arm" ], @@ -3402,9 +3415,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", - "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.24.2.tgz", + "integrity": "sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==", "cpu": [ "arm64" ], @@ -3419,9 +3432,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", - "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.24.2.tgz", + "integrity": "sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==", "cpu": [ "arm64" ], @@ -3436,9 +3449,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", - "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.24.2.tgz", + "integrity": "sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==", "cpu": [ "ppc64" ], @@ -3453,9 +3466,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", - "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.24.2.tgz", + "integrity": "sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==", "cpu": [ "riscv64" ], @@ -3470,9 +3483,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", - "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.24.2.tgz", + "integrity": "sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==", "cpu": [ "riscv64" ], @@ -3487,9 +3500,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", - "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.24.2.tgz", + "integrity": "sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==", "cpu": [ "s390x" ], @@ -3504,9 +3517,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", - "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.24.2.tgz", + "integrity": "sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==", "cpu": [ "x64" ], @@ -3521,9 +3534,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", - "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.24.2.tgz", + "integrity": "sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==", "cpu": [ "x64" ], @@ -3538,9 +3551,9 @@ ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", - "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.24.2.tgz", + "integrity": "sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==", "cpu": [ "arm64" ], @@ -3552,9 +3565,9 @@ ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", - "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.24.2.tgz", + "integrity": "sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==", "cpu": [ "wasm32" ], @@ -3562,41 +3575,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.11.0", - "@emnapi/runtime": "1.11.0", - "@napi-rs/wasm-runtime": "^1.1.5" + "@emnapi/core": "1.11.2", + "@emnapi/runtime": "1.11.2", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", - "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", - "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", - "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.24.2.tgz", + "integrity": "sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==", "cpu": [ "arm64" ], @@ -3608,9 +3598,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", - "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.24.2.tgz", + "integrity": "sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==", "cpu": [ "x64" ], @@ -3681,9 +3671,9 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", - "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.2.tgz", + "integrity": "sha512-l7x215OGvo1s52JWmR8U/DAVzEDWBCIbTm28aeJV/WDTSHgcKXaZTuBT0hJMs5NggilfJTW3clZVvd24yfKJxA==", "cpu": [ "arm64" ], @@ -3698,9 +3688,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", - "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.2.tgz", + "integrity": "sha512-9u9Xv6c1AJZT0FfwH5vrMG5Jjcwhc1MlyrPu0XfTqkzsmqfks2M6W/o5XwAJgVVN/jHpqqngC1WevHKKTIUtIA==", "cpu": [ "arm64" ], @@ -3715,9 +3705,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", - "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.2.tgz", + "integrity": "sha512-9W1mbGZAfW3oqd85bhBkmpyHCCzL1TeG/zFFP3vg7b0rlly8cxOcre5nXwz+LHazCwac2MNWgPdPCHndABjpWQ==", "cpu": [ "x64" ], @@ -3732,9 +3722,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", - "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.2.tgz", + "integrity": "sha512-0p1lhiCSCyaerFwtrdZQUx7NqGk6LQnaRKWX7tFQqwQgvX0rjM15cIkm3pax1UpEakK14C4mOxx/jSqCBdBRqQ==", "cpu": [ "x64" ], @@ -3749,9 +3739,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", - "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.2.tgz", + "integrity": "sha512-e+cOJXrJ2L3zx6YzqPg+f6Wbk3V1cKB8bOhbaYdVYN3DdquzNdRAmrbETz1qnt5yp/c7JNlNjmITiA2cVneQ7w==", "cpu": [ "arm" ], @@ -3766,9 +3756,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", - "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.2.tgz", + "integrity": "sha512-JsSMsj6sNat/MuhG5fnBD7QgbtpHKVe30x5/bAVirDHdhoQRXJkF6xc0Jqk8O4fiCUQAzMOoH9wZi3m60c8wtg==", "cpu": [ "arm64" ], @@ -3786,9 +3776,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", - "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.2.tgz", + "integrity": "sha512-B5G/zJdHaoJn9vD50eGHWkiWfmq8Uhi3IiLPJTzmZTrAalk1bztUikSXo0qga18ibE0IXboyeMUnhPjhAJ45wQ==", "cpu": [ "arm64" ], @@ -3806,9 +3796,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", - "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.2.tgz", + "integrity": "sha512-6mC/awzKka8W6EoekjegpfGkjz8jXWDX63pqu/HYVpyKtZfu65Jsh4QAH3Kej3CAv/c1oGX7psTmFEbr0mDxLA==", "cpu": [ "ppc64" ], @@ -3826,9 +3816,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", - "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.2.tgz", + "integrity": "sha512-412MX9fJLdA1IK28EZnc8jYv2HRTleOZgfLQumJ5zy7OeJLZlg/CETwFaXjNmGVxG51cFHpKLqb5LKvBC+HsHA==", "cpu": [ "s390x" ], @@ -3846,9 +3836,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", - "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.2.tgz", + "integrity": "sha512-Q/+HI/ToJafZ1iCqGgVQXUEkIjufHCTF0gBQ2a5o3cg7GJ2h0qyq3nvvSmU+bGda2/7ygXpTY4TM6gO9OhQ0ZA==", "cpu": [ "x64" ], @@ -3866,9 +3856,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", - "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.2.tgz", + "integrity": "sha512-ZKp/w41n6wCvxzxQHtQSbuphfX3Y4cCvbjkKHusrLx4lh+JWLTU7StSltO/DKARISzbj368d+qUaCjI8K2wzXw==", "cpu": [ "x64" ], @@ -3886,9 +3876,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", - "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.2.tgz", + "integrity": "sha512-pxE6xD4KS3eAROkKK5yrhB9/3+vhlhVGMvlQLbdpzrBGDbKrnzx3RLwPaHvasLo6jgaiBLn7e04Df9C4tYhjmA==", "cpu": [ "arm64" ], @@ -3902,29 +3892,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", - "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", - "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.2.tgz", + "integrity": "sha512-4MqEue5re+xIZzAWsB8sj0P1kqZySWqIuN4t6QaIO/YA6SFwySOLruvWQFzfmqk8LBK2P30KCSJwf6mJCZZ5/A==", "cpu": [ "arm64" ], @@ -3939,9 +3910,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", - "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.2.tgz", + "integrity": "sha512-NweNxxD0Nf9t8v7kodun45Ijp3EIwYY+uydPP6qBEYvfBqhIjN6dZMzlQja3tqX/aLs3F3Uz+AxDpKgRhpOZQg==", "cpu": [ "x64" ], @@ -4642,16 +4613,29 @@ } }, "node_modules/@simple-libs/child-process-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", - "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", + "integrity": "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==", "dev": true, "license": "MIT", "dependencies": { - "@simple-libs/stream-utils": "^1.2.0" + "@simple-libs/stream-utils": "^2.0.0" }, "engines": { - "node": ">=18" + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@simple-libs/child-process-utils/node_modules/@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" }, "funding": { "url": "https://ko-fi.com/dangreen" @@ -4739,9 +4723,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.13.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz", - "integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "dev": true, "license": "MIT", "dependencies": { @@ -5106,14 +5090,14 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz", - "integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", + "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -5127,8 +5111,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.9", - "vitest": "4.1.9" + "@vitest/browser": "4.1.10", + "vitest": "4.1.10" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -5137,16 +5121,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -5155,13 +5139,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -5182,9 +5166,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -5195,13 +5179,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -5209,14 +5193,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -5225,9 +5209,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -5235,13 +5219,13 @@ } }, "node_modules/@vitest/ui": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.9.tgz", - "integrity": "sha512-U/cRvtqfEPj27FI1n9cyUvi4vXXdcLhjJiI+InYKdk8hP4VrS6RXOjGL7rfFaeBc37iRKANsR6eEzIoC7lmgBQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.10.tgz", + "integrity": "sha512-EOUqfXHTXtpSHsyLHH40ts3Ue+hRhSGwzwzMlK0dTEOLSDYyOXLyr5JDGmHQWhN2DYI30gw6dVx3cdgM9FZl+Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "fflate": "^0.8.2", "flatted": "^3.4.2", "pathe": "^2.0.3", @@ -5253,17 +5237,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.1.9" + "vitest": "4.1.10" } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -7083,23 +7067,6 @@ "traverse": "0.6.8" } }, - "node_modules/git-raw-commits": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.1.tgz", - "integrity": "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@conventional-changelog/git-client": "^2.6.0", - "meow": "^13.0.0" - }, - "bin": { - "git-raw-commits": "src/cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -7450,9 +7417,9 @@ } }, "node_modules/immutable": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.8.tgz", - "integrity": "sha512-TM5YqrGeTsVIPPpILzeqZ8D2Zc2TvNgSDi88zPF2a4cyqQdWV/wVWBDRDbNzzrLeRWScrFcOX9lW2iX6GOtUDw==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", "dev": true, "license": "MIT" }, @@ -7938,9 +7905,9 @@ } }, "node_modules/knip": { - "version": "6.24.0", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.24.0.tgz", - "integrity": "sha512-PokLlgeEjLh1rAsB7ts+52wZ37HBr1nDhE6NNONwEaXdeZGCJOkP7ZlIAI2Gtu8xohquzTWy75bc/1diI9shQw==", + "version": "6.31.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.31.0.tgz", + "integrity": "sha512-NbeIEmUS2VUMjAkbiSNOKPJeV9wpCsr0660sUyKyMQbk4Iom0++nTLInVp4MJ+LfR4kORnw67bDi5tvO7YLnzA==", "dev": true, "funding": [ { @@ -7958,15 +7925,15 @@ "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.137.0", - "oxc-resolver": "11.21.3", - "picomatch": "^4.0.4", - "smol-toml": "^1.6.1", + "oxc-parser": "^0.142.0", + "oxc-resolver": "11.24.2", + "picomatch": "^4.0.5", + "smol-toml": "^1.7.1", "strip-json-comments": "5.0.3", "tinyglobby": "^0.2.17", - "unbash": "^4.0.1", + "unbash": "^4.0.4", "yaml": "^2.9.0", - "zod": "^4.1.11" + "zod": "^4.4.3" }, "bin": { "knip": "bin/knip.js", @@ -7990,9 +7957,9 @@ } }, "node_modules/lefthook": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-2.1.9.tgz", - "integrity": "sha512-bwDaIOViTktE8kJLf9jP0p+H2/RDTlFFlc43Am2YgUsX22hI6Sq4RbzsrecwzY5y+MHTipOH7WsmWSEniePHWQ==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-2.1.10.tgz", + "integrity": "sha512-K7mM4WoqMwqfXYK11EHy+lSH1uW8XHni3Yn/bSqyerPkUPygGdf3xn18JoV5HyA06xuQL3ofGAOjG01QX9oJ4w==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -8000,22 +7967,22 @@ "lefthook": "bin/index.js" }, "optionalDependencies": { - "lefthook-darwin-arm64": "2.1.9", - "lefthook-darwin-x64": "2.1.9", - "lefthook-freebsd-arm64": "2.1.9", - "lefthook-freebsd-x64": "2.1.9", - "lefthook-linux-arm64": "2.1.9", - "lefthook-linux-x64": "2.1.9", - "lefthook-openbsd-arm64": "2.1.9", - "lefthook-openbsd-x64": "2.1.9", - "lefthook-windows-arm64": "2.1.9", - "lefthook-windows-x64": "2.1.9" + "lefthook-darwin-arm64": "2.1.10", + "lefthook-darwin-x64": "2.1.10", + "lefthook-freebsd-arm64": "2.1.10", + "lefthook-freebsd-x64": "2.1.10", + "lefthook-linux-arm64": "2.1.10", + "lefthook-linux-x64": "2.1.10", + "lefthook-openbsd-arm64": "2.1.10", + "lefthook-openbsd-x64": "2.1.10", + "lefthook-windows-arm64": "2.1.10", + "lefthook-windows-x64": "2.1.10" } }, "node_modules/lefthook-darwin-arm64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-2.1.9.tgz", - "integrity": "sha512-119HryNcvr4nqn0wUIrNPgpMEPn9yMQzEcW/lezRsnb56PCJriJB92+MCySPVcWDxJnZef7o0T3jdnPNiSH7Qg==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-2.1.10.tgz", + "integrity": "sha512-nw+X8wRNDoUUV6WSteyKBbcLySq+fsmZt5WV/s50ZJpysmsDKJOUMln6SllNfP+60dzUahAO7REco/2633BsLg==", "cpu": [ "arm64" ], @@ -8027,9 +7994,9 @@ ] }, "node_modules/lefthook-darwin-x64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-2.1.9.tgz", - "integrity": "sha512-dwo5Tke2XcQCM56DGHgFKBfRbJIL6xs2wZ0zG1TUVZgl4t4mQUt6LiZ4V/ZQfYHTZF9qywvXoIlR5N35qOaiVQ==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-2.1.10.tgz", + "integrity": "sha512-KQ/bHmvpkFdHMn4pZnUdTf+GuSC+aBBgBTxZT4GW+6cSf+qbErKZBhK7cH6BmILsvx43+VzEArvHYY7YOfRFOQ==", "cpu": [ "x64" ], @@ -8041,9 +8008,9 @@ ] }, "node_modules/lefthook-freebsd-arm64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-2.1.9.tgz", - "integrity": "sha512-+09PVap6nl6xsaHch5JLtq7WvIR++U1Q2MzA2ai0M4uB/VP3AqrvKqHw6+9hjyKnIH+HHL83uqi77EAY+LaxLA==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-2.1.10.tgz", + "integrity": "sha512-8su6DwydP7+pv7kG0zCtjphqsw4ouOnfexRUErapy5GTxYBoUOhYz3RSHTSWNRsK6W4jva7FPUh2Lp5/PSn30w==", "cpu": [ "arm64" ], @@ -8055,9 +8022,9 @@ ] }, "node_modules/lefthook-freebsd-x64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-2.1.9.tgz", - "integrity": "sha512-8XresjKIYpkE9ARgCtBEZgJZxAU3T4MIqzj4zNy15XRT59I1Us+QdqXTNm+pkZ41Yd2X/nxs2Pkvbq3NWWlIGw==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-2.1.10.tgz", + "integrity": "sha512-GeAJEFxko3Lk+AsnS3NleAFrpyMLFUKOlgJvPKuU0xHwVEI/z+ZoCcmuO0BX+4CS0NLbZhC/YQAvBASqDvvVdQ==", "cpu": [ "x64" ], @@ -8069,9 +8036,9 @@ ] }, "node_modules/lefthook-linux-arm64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-2.1.9.tgz", - "integrity": "sha512-1oNIQfwrPe6rgU2KcDM3aF6+hpZDCKx1TmawQKpXUY5gVsbZ7MqX0Sk/1lnnWxqPm+kQQ5f6J2dpFWd+4xH8jg==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-2.1.10.tgz", + "integrity": "sha512-1sHTCmpTWjVMs+yKPBLRNT1kuuIr1yjietlk7rCB6wFPVOS6Ph3o2zPFH2AvW1UymHlqwyHXzBr9EtDpQ7j1mQ==", "cpu": [ "arm64" ], @@ -8083,9 +8050,9 @@ ] }, "node_modules/lefthook-linux-x64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-2.1.9.tgz", - "integrity": "sha512-fT+7Q+BJyGp+CslFQkNXmdFRgyVXsPHPi9NAsDX0a6QOyNnoORByAsvx6zeAKuF5rL3BBgNfho1/v2RuGxGy9w==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-2.1.10.tgz", + "integrity": "sha512-z/VlRB3bh6mBvW3r1rwnJ5vP8z+Krx5gJzkZ4veDXh+6FlRTx8wtd3g3fllOv/yZMxkgmL3fQoFXv05Esa7vBQ==", "cpu": [ "x64" ], @@ -8097,9 +8064,9 @@ ] }, "node_modules/lefthook-openbsd-arm64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-2.1.9.tgz", - "integrity": "sha512-4bVuafBk3dddVNo0+3hMbjcJs4mqYAstxpPMmX2ufkudSTYFNIhWoqwuGVQV/SS/xdcOKJAldW4qayAzed2ysw==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-2.1.10.tgz", + "integrity": "sha512-430zL8sSIKw5P0YXGG6PB+eAhHa06n0PXuaERaAQE4Ss3odfqwnl5Mq9hQmkEnOS1EGiQEKkd0UHv/i4PtMNIQ==", "cpu": [ "arm64" ], @@ -8111,9 +8078,9 @@ ] }, "node_modules/lefthook-openbsd-x64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-2.1.9.tgz", - "integrity": "sha512-PmPoMmLP/wQQWcQ9u2YH86bTZ3UCfBsxuEmVTEyPU2U8R1qSTp5r/Gs3G8cN5Mxo91XB9oBERtF1n+xD3W6aVA==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-2.1.10.tgz", + "integrity": "sha512-bgkO8PphGZVDhQgCJ524aYYPI5491pVmCiLPGjBIo1AvOSlIyw4N1Y+1C3QfqwEmechzw+Aq16SNc8pqv6UuXg==", "cpu": [ "x64" ], @@ -8125,9 +8092,9 @@ ] }, "node_modules/lefthook-windows-arm64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-2.1.9.tgz", - "integrity": "sha512-KphfkBKmwBnmolyrdhIl3lrBaOyTcCgXBT2AB/9OHnEXhOLvv5uTCUkrD4YRAxXPtFKq6UvnapIeoL3GZq0bdA==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-2.1.10.tgz", + "integrity": "sha512-5Q6etF0Fla2DDA4ilDySrdNgiR5+W7cJZwnZ69Je3kvWCaWm4wnkuc8FEdjp3kiL2x3ZXipdI00f5vpO8aWmog==", "cpu": [ "arm64" ], @@ -8139,9 +8106,9 @@ ] }, "node_modules/lefthook-windows-x64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-2.1.9.tgz", - "integrity": "sha512-2qlUtkJHZ3MyUxgV5XTEmcrIoNZA07iwaquoswAcqv/1MeBFXlD+O+koFRfrzWng2O5WYEbpJnd8tvaYnV8fTA==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-2.1.10.tgz", + "integrity": "sha512-c/XH8YZtylG4XaxzqFfXluvq2LXq2W/p54Bnzn3+Z7E5X2Fk3JlFJAibulMbIt2+w8T7UI/r97ok5GqE4kGaeA==", "cpu": [ "x64" ], @@ -8153,9 +8120,9 @@ ] }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -8169,23 +8136,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -8204,9 +8171,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -8225,9 +8192,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -8246,9 +8213,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -8267,9 +8234,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -8288,9 +8255,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], @@ -8312,9 +8279,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], @@ -8336,9 +8303,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], @@ -8360,9 +8327,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], @@ -8384,9 +8351,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -8405,9 +8372,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -8978,9 +8945,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ { @@ -11052,13 +11019,13 @@ } }, "node_modules/oxc-parser": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", - "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.142.0.tgz", + "integrity": "sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.137.0" + "@oxc-project/types": "^0.142.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -11067,57 +11034,57 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.137.0", - "@oxc-parser/binding-android-arm64": "0.137.0", - "@oxc-parser/binding-darwin-arm64": "0.137.0", - "@oxc-parser/binding-darwin-x64": "0.137.0", - "@oxc-parser/binding-freebsd-x64": "0.137.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", - "@oxc-parser/binding-linux-arm64-musl": "0.137.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", - "@oxc-parser/binding-linux-x64-gnu": "0.137.0", - "@oxc-parser/binding-linux-x64-musl": "0.137.0", - "@oxc-parser/binding-openharmony-arm64": "0.137.0", - "@oxc-parser/binding-wasm32-wasi": "0.137.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", - "@oxc-parser/binding-win32-x64-msvc": "0.137.0" + "@oxc-parser/binding-android-arm-eabi": "0.142.0", + "@oxc-parser/binding-android-arm64": "0.142.0", + "@oxc-parser/binding-darwin-arm64": "0.142.0", + "@oxc-parser/binding-darwin-x64": "0.142.0", + "@oxc-parser/binding-freebsd-x64": "0.142.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.142.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.142.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.142.0", + "@oxc-parser/binding-linux-arm64-musl": "0.142.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.142.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.142.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.142.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.142.0", + "@oxc-parser/binding-linux-x64-gnu": "0.142.0", + "@oxc-parser/binding-linux-x64-musl": "0.142.0", + "@oxc-parser/binding-openharmony-arm64": "0.142.0", + "@oxc-parser/binding-wasm32-wasi": "0.142.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.142.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.142.0", + "@oxc-parser/binding-win32-x64-msvc": "0.142.0" } }, "node_modules/oxc-resolver": { - "version": "11.21.3", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", - "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.24.2.tgz", + "integrity": "sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.21.3", - "@oxc-resolver/binding-android-arm64": "11.21.3", - "@oxc-resolver/binding-darwin-arm64": "11.21.3", - "@oxc-resolver/binding-darwin-x64": "11.21.3", - "@oxc-resolver/binding-freebsd-x64": "11.21.3", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", - "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", - "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", - "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", - "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", - "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", - "@oxc-resolver/binding-linux-x64-musl": "11.21.3", - "@oxc-resolver/binding-openharmony-arm64": "11.21.3", - "@oxc-resolver/binding-wasm32-wasi": "11.21.3", - "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", - "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" + "@oxc-resolver/binding-android-arm-eabi": "11.24.2", + "@oxc-resolver/binding-android-arm64": "11.24.2", + "@oxc-resolver/binding-darwin-arm64": "11.24.2", + "@oxc-resolver/binding-darwin-x64": "11.24.2", + "@oxc-resolver/binding-freebsd-x64": "11.24.2", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.24.2", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.24.2", + "@oxc-resolver/binding-linux-arm64-gnu": "11.24.2", + "@oxc-resolver/binding-linux-arm64-musl": "11.24.2", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.24.2", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.24.2", + "@oxc-resolver/binding-linux-riscv64-musl": "11.24.2", + "@oxc-resolver/binding-linux-s390x-gnu": "11.24.2", + "@oxc-resolver/binding-linux-x64-gnu": "11.24.2", + "@oxc-resolver/binding-linux-x64-musl": "11.24.2", + "@oxc-resolver/binding-openharmony-arm64": "11.24.2", + "@oxc-resolver/binding-wasm32-wasi": "11.24.2", + "@oxc-resolver/binding-win32-arm64-msvc": "11.24.2", + "@oxc-resolver/binding-win32-x64-msvc": "11.24.2" } }, "node_modules/p-each-series": { @@ -11405,9 +11372,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -11442,9 +11409,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -11462,7 +11429,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -11747,13 +11714,13 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", - "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.2.tgz", + "integrity": "sha512-opwpo1tQBAcpSUJDt94B7hhLNGOKjCdE//XXjeLrnx9b83bjnw45tXdg1b09yEw/VLFBJGZpwRULMmOZo7ol+A==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.137.0", + "@oxc-project/types": "=0.142.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -11763,21 +11730,20 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.3", - "@rolldown/binding-darwin-arm64": "1.1.3", - "@rolldown/binding-darwin-x64": "1.1.3", - "@rolldown/binding-freebsd-x64": "1.1.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", - "@rolldown/binding-linux-arm64-gnu": "1.1.3", - "@rolldown/binding-linux-arm64-musl": "1.1.3", - "@rolldown/binding-linux-ppc64-gnu": "1.1.3", - "@rolldown/binding-linux-s390x-gnu": "1.1.3", - "@rolldown/binding-linux-x64-gnu": "1.1.3", - "@rolldown/binding-linux-x64-musl": "1.1.3", - "@rolldown/binding-openharmony-arm64": "1.1.3", - "@rolldown/binding-wasm32-wasi": "1.1.3", - "@rolldown/binding-win32-arm64-msvc": "1.1.3", - "@rolldown/binding-win32-x64-msvc": "1.1.3" + "@rolldown/binding-android-arm64": "1.2.2", + "@rolldown/binding-darwin-arm64": "1.2.2", + "@rolldown/binding-darwin-x64": "1.2.2", + "@rolldown/binding-freebsd-x64": "1.2.2", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.2", + "@rolldown/binding-linux-arm64-gnu": "1.2.2", + "@rolldown/binding-linux-arm64-musl": "1.2.2", + "@rolldown/binding-linux-ppc64-gnu": "1.2.2", + "@rolldown/binding-linux-s390x-gnu": "1.2.2", + "@rolldown/binding-linux-x64-gnu": "1.2.2", + "@rolldown/binding-linux-x64-musl": "1.2.2", + "@rolldown/binding-openharmony-arm64": "1.2.2", + "@rolldown/binding-win32-arm64-msvc": "1.2.2", + "@rolldown/binding-win32-x64-msvc": "1.2.2" } }, "node_modules/run-parallel": { @@ -11819,9 +11785,9 @@ "license": "MIT" }, "node_modules/semantic-release": { - "version": "25.0.5", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-25.0.5.tgz", - "integrity": "sha512-mn61SUJwtM8ThrWn2WmgLVpwVJeG/hPSupua1psdMoufmwRIPyvRLkRkL0JDXkP67OntlLWUYnBnfVc8EDO3/g==", + "version": "25.0.8", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-25.0.8.tgz", + "integrity": "sha512-w/iZ0bur36rKffXZYmIUmy068eoBY3Ij1DCCddx2JwWEM5Tg+eU9ld/E9qSInVvPASyyR2Ln/XGfQ9OZrMlhtw==", "dev": true, "license": "MIT", "dependencies": { @@ -11998,9 +11964,9 @@ } }, "node_modules/semantic-release/node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -12161,9 +12127,9 @@ } }, "node_modules/semantic-release/node_modules/type-fest": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", - "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz", + "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==", "dev": true, "license": "(MIT OR CC0-1.0)", "dependencies": { @@ -12448,9 +12414,9 @@ } }, "node_modules/smol-toml": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", - "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz", + "integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -12992,9 +12958,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.22.5", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.5.tgz", - "integrity": "sha512-F7JnSfPl5ASt6LqwWyUQ3T8BwN3q0eQEbFMYa2iRWaVQmmudo0d7fRmwM4O002gsvW1bs0yBYioutsAjqLJMvQ==", + "version": "4.23.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.5.tgz", + "integrity": "sha512-rw55FUaqOoI7RvlQwLbhO4nSDApnQ4/CykPuiQ/EPvtrX3WA9Ig55jIt9VvbBJbzJuj12ueRu4PMZ2SxPVbihg==", "dev": true, "license": "MIT", "dependencies": { @@ -13083,9 +13049,9 @@ } }, "node_modules/unbash": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.2.tgz", - "integrity": "sha512-8gwNZ29+0/3zmXw7ToIHZtg6wK37xnniRUdBt7B27xZxaxfgR5tGMaGHT0t0dLtBV9fXE7zurh0s6Z1DHVjfWg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.5.tgz", + "integrity": "sha512-EE9xv9cr93DSppe086Rnbq0jwG7MBCEe22JZ4UQ8Bn9RyUwDSoUpBmzdb5+7PzocdqBEb/K73FGPaQUMmLxTQQ==", "dev": true, "license": "ISC", "engines": { @@ -13254,16 +13220,16 @@ } }, "node_modules/vite": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", - "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", + "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "~1.1.2", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", "tinyglobby": "^0.2.17" }, "bin": { @@ -13280,7 +13246,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", + "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -13332,19 +13298,19 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -13372,12 +13338,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" From 7986a5d8c1c65b20505097e9f254fb9497fe3aa4 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:04:50 +0200 Subject: [PATCH 27/34] ci(setup): pin npm 11 so every matrix Node version matches the lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Node 22 leg of the unit-test matrix fails `npm ci` with EUSAGE whenever the lockfile is refreshed: npm error Missing: conventional-commits-filter@6.0.1 from lock file npm error Missing: conventional-commits-parser@7.1.2 from lock file npm error Missing: argue-cli@3.1.0 from lock file The cause is the npm version, not the Node version. Node 22 bundles npm 10.9, Node 24 bundles npm 11, and package-lock.json is written by npm 11 (the project default and Renovate). The two majors disagree on optional peerDependencies: `@conventional-changelog/git-client` 3.x declares optional peers on conventional-commits-filter ^6.0.1 and conventional-commits-parser ^7.0.1, npm 11 omits them from the tree, and npm 10 insists on materializing them (plus argue-cli, a transitive dep of the parser) — then aborts because they are not in the lockfile. Install npm 11 in the shared setup action so the installer is identical on every Node version. The matrix keeps testing Node 22 at runtime, which is what the engines floor is about; only the package manager is normalized. Alternatives considered: regenerating the lockfile under npm 10 makes CI pass once but regresses on the next npm 11 `npm install`, and dropping Node 22 from the matrix would give up engines-floor coverage. `engines.npm` was deliberately not added to package.json — it would emit EBADENGINE warnings for consumers installing the published package with npm 10. Refs #277 --- .github/actions/setup/action.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index b719e9a3..e089d45d 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -34,6 +34,15 @@ runs: cache: npm registry-url: ${{ inputs.registry-url }} + # package-lock.json is written by npm 11 (project default and Renovate). + # Node 22 bundles npm 10, which materializes optional peerDependencies that + # npm 11 omits from the lockfile, so `npm ci` there fails with EUSAGE. Pin + # the npm major so every matrix Node version agrees with the committed + # lockfile; the matrix still exercises the engines floor at runtime. + - name: Pin npm major + shell: bash + run: npm install -g npm@11 + # `npm ci` runs `prepare`, which is CI-guarded (scripts/prepare.mjs) so it # does NOT hit the live Linear GraphQL schema here. Jobs needing generated # types must run the build step below (or `npm pack`). From afcd209f8620d2711574ab71eda8bed3d38aaf57 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:04:55 +0200 Subject: [PATCH 28/34] test(output): assert the own __proto__ descriptor without optional chaining MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Biome 2.5.6 flags `(ownProto?.value as { x: number }).x` with lint/correctness/noUnsafeOptionalChaining: if the descriptor lookup returned undefined the chain short-circuits and the member access throws a TypeError instead of failing the assertion. Assert on the descriptor value directly. Coverage is unchanged — a missing descriptor yields undefined, which still fails toEqual — and the cast goes away with it. Refs #277 --- tests/unit/common/output.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/common/output.test.ts b/tests/unit/common/output.test.ts index 03e8c362..0024935e 100644 --- a/tests/unit/common/output.test.ts +++ b/tests/unit/common/output.test.ts @@ -183,7 +183,7 @@ describe("pickFields", () => { expect(Object.getPrototypeOf(result)).toBe(Object.prototype); expect((result as { a: number }).a).toBe(1); const ownProto = Object.getOwnPropertyDescriptor(result, "__proto__"); - expect((ownProto?.value as { x: number }).x).toBe(9); + expect(ownProto?.value).toEqual({ x: 9 }); }); }); From a9f8758084c5bc0524473c4b7bffb60280b84a48 Mon Sep 17 00:00:00 2001 From: Fabian Jocks <24557998+iamfj@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:05:12 +0200 Subject: [PATCH 29/34] style(biome): adopt the 2.5.6 formatting and migrate biome.json The dev-dependency bump takes Biome from 2.5.2 to 2.5.6, which changes how long `it.each([...])(name, callback)` calls are broken across lines and makes `check:ci` fail on cli-errors.test.ts. Apply the new formatter output; the change is mechanical, no assertion is touched. Also run `biome migrate`: the `$schema` URL was still pinned at 2.4.11 (nothing updates it when Renovate bumps the package), and `linter.rules.recommended` is deprecated in favour of `linter.rules.preset`. Both were emitted as infos by `biome check`; migrating now keeps the config in step with the CLI that the lockfile installs. The useLiteralKeys and noNonNullAssertion overrides are unchanged. Refs #277 --- biome.json | 4 +- tests/unit/common/cli-errors.test.ts | 61 +++++++++++++++------------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/biome.json b/biome.json index 236ec8c7..8ff28c81 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.11/schema.json", + "$schema": "https://biomejs.dev/schemas/2.5.6/schema.json", "vcs": { "enabled": true, "clientKind": "git", @@ -23,7 +23,7 @@ }, "linter": { "rules": { - "recommended": true, + "preset": "recommended", "complexity": { "useLiteralKeys": "off" }, diff --git a/tests/unit/common/cli-errors.test.ts b/tests/unit/common/cli-errors.test.ts index 6bcb4ca4..e57dfbcd 100644 --- a/tests/unit/common/cli-errors.test.ts +++ b/tests/unit/common/cli-errors.test.ts @@ -371,35 +371,38 @@ describe("interceptParseErrors + handleParseFailure", () => { it.each([ [["issues", "help"], "linearis issues"], [["issues", "threads", "help"], "linearis issues threads"], - ])("treats %s as an unknown command, not implicit help", async (argv, path) => { - await runCli(argv); - - expect(emittedPayload()).toMatchObject({ - error: "UNKNOWN_COMMAND", - message: `Unknown command "help" for "${path}".`, - command: path, - exit_code: 2, - }); - expect(emittedPayload().available_commands).not.toContain("help"); - expect(exitSpy).toHaveBeenCalledWith(2); - expect(stdoutSpy).not.toHaveBeenCalled(); - }); - - it.each([ - ["--help"], - ["--version"], - ])("leaves %s untouched and exits 0", async (arg) => { - await runCli([arg]); - - expect(consoleErrorSpy).not.toHaveBeenCalled(); - expect(stderrSpy).not.toHaveBeenCalled(); - expect(exitSpy).toHaveBeenCalledWith(0); - expect(exitSpy).not.toHaveBeenCalledWith(2); - // Both throw an exit-code-0 CommanderError after writing to stdout. - expect( - stdoutSpy.mock.calls.length + consoleLogSpy.mock.calls.length, - ).toBeGreaterThan(0); - }); + ])( + "treats %s as an unknown command, not implicit help", + async (argv, path) => { + await runCli(argv); + + expect(emittedPayload()).toMatchObject({ + error: "UNKNOWN_COMMAND", + message: `Unknown command "help" for "${path}".`, + command: path, + exit_code: 2, + }); + expect(emittedPayload().available_commands).not.toContain("help"); + expect(exitSpy).toHaveBeenCalledWith(2); + expect(stdoutSpy).not.toHaveBeenCalled(); + }, + ); + + it.each([["--help"], ["--version"]])( + "leaves %s untouched and exits 0", + async (arg) => { + await runCli([arg]); + + expect(consoleErrorSpy).not.toHaveBeenCalled(); + expect(stderrSpy).not.toHaveBeenCalled(); + expect(exitSpy).toHaveBeenCalledWith(0); + expect(exitSpy).not.toHaveBeenCalledWith(2); + // Both throw an exit-code-0 CommanderError after writing to stdout. + expect( + stdoutSpy.mock.calls.length + consoleLogSpy.mock.calls.length, + ).toBeGreaterThan(0); + }, + ); it("still exits 0 for the root overview", async () => { await runCli([]); From 05bb7af33f14b5ab91e6c9797d613ac192c58c80 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:10:00 +0000 Subject: [PATCH 30/34] fix(deps): update dependency graphql to v17 --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index f590aa13..8a1602ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "commander": "15.0.0", - "graphql": "16.14.2", + "graphql": "17.0.2", "node-emoji": "2.2.0" }, "bin": { @@ -7125,12 +7125,12 @@ "license": "ISC" }, "node_modules/graphql": { - "version": "16.14.2", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", - "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-17.0.2.tgz", + "integrity": "sha512-FRWbddMxfkjiB7z+aQDWIR+E34xo9I8c9mtK2RPv8PmMzKRvrdsreHL/Ui/TmwHJfhHChEtsFPyMHKI+xuarQQ==", "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + "node": "^22.0.0 || ^24.0.0 || ^25.0.0 || >=26.0.0" } }, "node_modules/graphql-config": { diff --git a/package.json b/package.json index b419d6b3..d3f38dd9 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "homepage": "https://github.com/linearis-oss/linearis#readme", "dependencies": { "commander": "15.0.0", - "graphql": "16.14.2", + "graphql": "17.0.2", "node-emoji": "2.2.0" }, "devDependencies": { From f4b13f72f9363cd972db6ec07167cd7718004cb2 Mon Sep 17 00:00:00 2001 From: semantic-release-bot <semantic-release-bot@martynus.net> Date: Thu, 6 Aug 2026 11:23:09 +0000 Subject: [PATCH 31/34] chore(release): 2026.7.0-next.5 [skip ci] ## [2026.7.0-next.5](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.4...v2026.7.0-next.5) (2026-08-06) --- CHANGELOG.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2878da84..72c2ad66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [2026.7.0-next.5](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.4...v2026.7.0-next.5) (2026-08-06) + ## [2026.7.0-next.4](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.3...v2026.7.0-next.4) (2026-08-06) ## [2026.7.0-next.3](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.2...v2026.7.0-next.3) (2026-08-06) diff --git a/package-lock.json b/package-lock.json index 8a1602ec..b88fb907 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "linearis", - "version": "2026.7.0-next.4", + "version": "2026.7.0-next.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "linearis", - "version": "2026.7.0-next.4", + "version": "2026.7.0-next.5", "license": "MIT", "dependencies": { "commander": "15.0.0", diff --git a/package.json b/package.json index d3f38dd9..cad565d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linearis", - "version": "2026.7.0-next.4", + "version": "2026.7.0-next.5", "description": "CLI tool for Linear.app with JSON output, smart ID resolution, and optimized GraphQL queries. Designed for LLM agents and humans who prefer structured data.", "main": "dist/main.js", "type": "module", From dfe97b8f0c3cf15c5fc40a7861f2cd422fdc30b9 Mon Sep 17 00:00:00 2001 From: Jack Hutson <agent@jackhutson.com> Date: Thu, 6 Aug 2026 00:00:33 -0400 Subject: [PATCH 32/34] fix(projects): bound project query connections to avoid complexity limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linear's complexity estimator charges an unbounded connection at its default page size (50) per parent row. ProjectListFields selected unbounded teams and labels connections, pricing the default `projects list` (first: 100) at ~13950 against a budget of 10000 — so default-limit lists failed with "Query too complex" even on near-empty workspaces, since the estimator prices page size, not data. - bound teams/labels (25) in ProjectListFields, members/initiatives (25) in ProjectDetailFields, and projectMilestones (25) in the default-connections fragment; the same list query now prices ~7450 - lower the projects read issues default from 50 to 25: each issue in the response costs ~260 (CompleteIssueFields carries four unbounded connections), so the old default read exceeded the budget on real workspaces - replace the update --label-mode add|remove pre-read (full project detail including milestones and issues) with a lean label-IDs-only query Closes #276 Closes #283 --- graphql/queries/projects.graphql | 33 +++++++++++++++++---- src/commands/projects.ts | 19 +++++------- src/services/project-service.ts | 26 ++++++++++++++-- tests/unit/commands/projects.test.ts | 23 +++++++------- tests/unit/services/project-service.test.ts | 29 +++++++++++++++++- 5 files changed, 98 insertions(+), 32 deletions(-) diff --git a/graphql/queries/projects.graphql b/graphql/queries/projects.graphql index 81889fac..1e9534d1 100644 --- a/graphql/queries/projects.graphql +++ b/graphql/queries/projects.graphql @@ -12,6 +12,12 @@ # Fields returned for each project in list results. Includes status, # priority, lead, teams, labels, health, and progress for rich output. # Retains deprecated `state` field for backward compatibility. +# +# Nested connections are explicitly bounded: Linear's complexity +# estimator charges an unbounded connection at its default page size +# (50) per parent row, which pushed the default `projects list` +# (first: 100) to ~13950 against a 10000 budget — the "Query too +# complex" error in #276. Bounds of 25 keep the same query at ~7450. fragment ProjectListFields on Project { id name @@ -34,14 +40,14 @@ fragment ProjectListFields on Project { id name } - teams { + teams(first: 25) { nodes { id key name } } - labels { + labels(first: 25) { nodes { id name @@ -69,13 +75,13 @@ fragment ProjectDetailFields on Project { id name } - members { + members(first: 25) { nodes { id name } } - initiatives { + initiatives(first: 25) { nodes { id name @@ -85,7 +91,7 @@ fragment ProjectDetailFields on Project { fragment ProjectDetailWithDefaultConnectionsFields on Project { ...ProjectDetailFields - projectMilestones { + projectMilestones(first: 25) { nodes { id name @@ -196,6 +202,23 @@ query GetProjectStatuses { } } +# Get only a project's label IDs +# +# Lean lookup used by `projects update --label-mode add|remove`, which +# previously fetched the full project detail (milestones + issues) just +# to read the current label set — an over-budget query on real +# workspaces (#283). +query GetProjectLabelIds($id: String!) { + project(id: $id) { + id + labels(first: 250) { + nodes { + id + } + } + } +} + # Find projects by case-insensitive name for ID resolution # # Returns up to two matches so the resolver can throw on ambiguity. diff --git a/src/commands/projects.ts b/src/commands/projects.ts index 085f1309..23a53453 100644 --- a/src/commands/projects.ts +++ b/src/commands/projects.ts @@ -37,6 +37,7 @@ import { createProject, deleteProject, getProject, + getProjectLabelIds, listProjects, type UpdateProjectInput, unarchiveProject, @@ -283,7 +284,7 @@ export function setupProjectsCommands(program: Command): void { .option( "--issues-first <n>", "how many issues to fetch; 0 omits issues", - "50", + "25", ) .action( commandAction<[string, ReadOptions, Command]>( @@ -731,9 +732,9 @@ export function setupProjectsCommands(program: Command): void { const projectId = await resolveProjectId(ctx.gql, project); const needsLabelContext = options.labels && (labelMode === "add" || labelMode === "remove"); - const projectContext = needsLabelContext - ? await getProject(ctx.gql, projectId) - : undefined; + const currentLabelIds = needsLabelContext + ? await getProjectLabelIds(ctx.gql, projectId) + : []; const input: UpdateProjectInput = {}; @@ -813,15 +814,9 @@ export function setupProjectsCommands(program: Command): void { const labelIds = await resolveProjectLabelIds(ctx.gql, labelNames); if (labelMode === "add") { - const currentLabels = projectContext?.labels?.nodes - ? projectContext.labels.nodes.map((l) => asUuid(l.id)) - : []; - input.labelIds = [...new Set([...currentLabels, ...labelIds])]; + input.labelIds = [...new Set([...currentLabelIds, ...labelIds])]; } else if (labelMode === "remove") { - const currentLabels = projectContext?.labels?.nodes - ? projectContext.labels.nodes.map((l) => asUuid(l.id)) - : []; - input.labelIds = currentLabels.filter( + input.labelIds = currentLabelIds.filter( (id) => !labelIds.includes(id), ); } else { diff --git a/src/services/project-service.ts b/src/services/project-service.ts index 6c8c8297..2afe6ffe 100644 --- a/src/services/project-service.ts +++ b/src/services/project-service.ts @@ -1,5 +1,9 @@ import type { GraphQLClient } from "../client/graphql-client.js"; -import type { BrandUuidFields, UUID } from "../common/identifier.js"; +import { + asUuid, + type BrandUuidFields, + type UUID, +} from "../common/identifier.js"; import { requireMutationEntity, requireMutationSuccess, @@ -12,6 +16,7 @@ import { type CreateProjectMutation, DeleteProjectDocument, GetProjectDocument, + GetProjectLabelIdsDocument, type GetProjectQuery, GetProjectsDocument, type GetProjectsQuery, @@ -93,7 +98,11 @@ export interface ProjectDetailOptions { } const DEFAULT_PROJECT_MILESTONES_FIRST = 25; -const DEFAULT_PROJECT_ISSUES_FIRST = 50; +// 25 keeps the default read under Linear's 10000 complexity budget: each +// issue in the response costs ~260 (CompleteIssueFields carries four +// connections charged at their default page size), so 50 issues alone +// exceeded the budget and the default read failed on real workspaces (#276). +const DEFAULT_PROJECT_ISSUES_FIRST = 25; function connectionFirstOrOneWhenSkipped(value: number): number { return value === 0 ? 1 : value; @@ -140,6 +149,19 @@ export async function getProject( return result.project; } +export async function getProjectLabelIds( + client: GraphQLClient, + id: UUID, +): Promise<UUID[]> { + const result = await client.request(GetProjectLabelIdsDocument, { id }); + + if (!result.project) { + throw new Error(`Project with ID "${id}" not found`); + } + + return result.project.labels.nodes.map((label) => asUuid(label.id)); +} + export async function createProject( client: GraphQLClient, input: CreateProjectInput, diff --git a/tests/unit/commands/projects.test.ts b/tests/unit/commands/projects.test.ts index 7bb9a6f4..321ec669 100644 --- a/tests/unit/commands/projects.test.ts +++ b/tests/unit/commands/projects.test.ts @@ -38,6 +38,7 @@ vi.mock("../../../src/services/project-service.js", () => ({ archiveProject: vi.fn().mockResolvedValue({ id: "proj-1", name: "Archived" }), listProjects: vi.fn().mockResolvedValue({ nodes: [], pageInfo: {} }), getProject: vi.fn().mockResolvedValue({ id: "proj-1" }), + getProjectLabelIds: vi.fn().mockResolvedValue([]), createProject: vi.fn().mockResolvedValue({ id: "proj-new" }), deleteProject: vi.fn().mockResolvedValue({ id: "proj-1", success: true }), unarchiveProject: vi.fn().mockResolvedValue({ id: "proj-1", name: "Active" }), @@ -139,6 +140,7 @@ import { createProject, deleteProject, getProject, + getProjectLabelIds, listProjects, unarchiveProject, updateProject, @@ -204,7 +206,7 @@ describe("projects read", () => { expect(getProject).toHaveBeenCalledWith( expect.anything(), "resolved-project-uuid", - { milestonesFirst: 25, issuesFirst: 50 }, + { milestonesFirst: 25, issuesFirst: 25 }, ); expect(outputSuccess).toHaveBeenCalledWith({ id: "proj-1" }); }); @@ -1056,10 +1058,9 @@ describe("projects update", () => { }); it("adds labels without dropping existing project labels", async () => { - vi.mocked(getProject).mockResolvedValueOnce({ - id: "proj-1", - labels: { nodes: [{ id: "existing-label-uuid" }] }, - } as Awaited<ReturnType<typeof getProject>>); + vi.mocked(getProjectLabelIds).mockResolvedValueOnce([ + "existing-label-uuid", + ] as Awaited<ReturnType<typeof getProjectLabelIds>>); const program = createProgram(); await program.parseAsync([ @@ -1074,7 +1075,7 @@ describe("projects update", () => { "add", ]); - expect(getProject).toHaveBeenCalledWith( + expect(getProjectLabelIds).toHaveBeenCalledWith( expect.anything(), "resolved-project-uuid", ); @@ -1091,12 +1092,10 @@ describe("projects update", () => { }); it("removes selected project labels without clearing all labels", async () => { - vi.mocked(getProject).mockResolvedValueOnce({ - id: "proj-1", - labels: { - nodes: [{ id: "keep-label-uuid" }, { id: "resolved-label-uuid" }], - }, - } as Awaited<ReturnType<typeof getProject>>); + vi.mocked(getProjectLabelIds).mockResolvedValueOnce([ + "keep-label-uuid", + "resolved-label-uuid", + ] as Awaited<ReturnType<typeof getProjectLabelIds>>); const program = createProgram(); await program.parseAsync([ diff --git a/tests/unit/services/project-service.test.ts b/tests/unit/services/project-service.test.ts index 147befe3..0c74e223 100644 --- a/tests/unit/services/project-service.test.ts +++ b/tests/unit/services/project-service.test.ts @@ -7,6 +7,7 @@ import { asUuid } from "../../../src/common/identifier.js"; import { ArchiveProjectDocument, GetProjectDocument, + GetProjectLabelIdsDocument, GetProjectWithReactionsDocument, } from "../../../src/gql/graphql.js"; import { @@ -14,6 +15,7 @@ import { createProject, deleteProject, getProject, + getProjectLabelIds, listProjects, unarchiveProject, updateProject, @@ -290,7 +292,7 @@ describe("getProject", () => { id: "proj-1", milestonesFirst: 25, skipMilestones: false, - issuesFirst: 50, + issuesFirst: 25, skipIssues: false, }); }); @@ -347,6 +349,31 @@ describe("getProject", () => { }); }); +describe("getProjectLabelIds", () => { + it("returns the project's label UUIDs via the lean query", async () => { + const client = mockGqlClient({ + project: { + id: "proj-1", + labels: { nodes: [{ id: "label-1" }, { id: "label-2" }] }, + }, + }); + + const result = await getProjectLabelIds(client, asUuid("proj-1")); + + expect(result).toEqual(["label-1", "label-2"]); + expect(client.request).toHaveBeenCalledWith(GetProjectLabelIdsDocument, { + id: "proj-1", + }); + }); + + it("throws when project not found", async () => { + const client = mockGqlClient({ project: null }); + await expect( + getProjectLabelIds(client, asUuid("nonexistent")), + ).rejects.toThrow('Project with ID "nonexistent" not found'); + }); +}); + describe("createProject", () => { it("returns created project", async () => { const client = mockGqlClient({ From a3145c1b99273c11b1e7f077cf2799f63ae4c141 Mon Sep 17 00:00:00 2001 From: Jack Hutson <agent@jackhutson.com> Date: Thu, 6 Aug 2026 11:59:28 -0400 Subject: [PATCH 33/34] fix(projects): surface truncation on bounded connections, lock bounds in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-ups for #284: - assert in tests that every bounded fragment connection carries a literal first: argument, so a future tidy-up that unbounds them fails CI instead of reintroducing #276 - select pageInfo.hasNextPage on the five bounded connections in the detail fragment so truncated pages are detectable in read and mutation responses; measured live, pageInfo prices at ~23 complexity per parent row, so the signal lives only where the parent count is one — selecting it in ProjectListFields put the default list back over budget (12120) and is deliberately omitted there - getProjectLabelIds now selects hasNextPage on its first: 250 read (Linear's per-connection maximum) and throws on truncation instead of letting a partial label set be written back as complete via the full-replacement labelIds input Refs #284 --- graphql/queries/projects.graphql | 34 ++++++ src/services/project-service.ts | 9 ++ tests/unit/services/project-service.test.ts | 115 +++++++++++++++++++- 3 files changed, 157 insertions(+), 1 deletion(-) diff --git a/graphql/queries/projects.graphql b/graphql/queries/projects.graphql index 1e9534d1..b8d9c310 100644 --- a/graphql/queries/projects.graphql +++ b/graphql/queries/projects.graphql @@ -61,8 +61,25 @@ fragment ProjectListFields on Project { # Extended fields for single-project read view. Includes content, # members, milestones, initiatives, creator, lifecycle dates, and # visual properties. +# +# Every bounded connection selects pageInfo.hasNextPage here so a +# truncated page is detectable. The teams/labels re-selections merge +# with ProjectListFields (identical first: arguments), adding the +# truncation signal only in detail/mutation contexts: pageInfo prices +# at ~23 complexity per parent row, which is negligible on a single +# project but would put the 100-row list back over the 10000 budget. fragment ProjectDetailFields on Project { ...ProjectListFields + teams(first: 25) { + pageInfo { + hasNextPage + } + } + labels(first: 25) { + pageInfo { + hasNextPage + } + } content icon color @@ -80,12 +97,18 @@ fragment ProjectDetailFields on Project { id name } + pageInfo { + hasNextPage + } } initiatives(first: 25) { nodes { id name } + pageInfo { + hasNextPage + } } } @@ -97,6 +120,9 @@ fragment ProjectDetailWithDefaultConnectionsFields on Project { name targetDate } + pageInfo { + hasNextPage + } } } @@ -208,6 +234,11 @@ query GetProjectStatuses { # previously fetched the full project detail (milestones + issues) just # to read the current label set — an over-budget query on real # workspaces (#283). +# +# 250 is Linear's per-connection maximum, so this read cannot be raised +# further; hasNextPage lets the service refuse a truncated label set +# instead of writing it back as if it were complete (labelIds is a +# full-replacement input). query GetProjectLabelIds($id: String!) { project(id: $id) { id @@ -215,6 +246,9 @@ query GetProjectLabelIds($id: String!) { nodes { id } + pageInfo { + hasNextPage + } } } } diff --git a/src/services/project-service.ts b/src/services/project-service.ts index 2afe6ffe..4482d3c2 100644 --- a/src/services/project-service.ts +++ b/src/services/project-service.ts @@ -159,6 +159,15 @@ export async function getProjectLabelIds( throw new Error(`Project with ID "${id}" not found`); } + // labelIds is a full-replacement input on projectUpdate; merging from a + // truncated read would silently delete every label past the page limit. + if (result.project.labels.pageInfo.hasNextPage) { + throw new Error( + `Project with ID "${id}" has more labels than a single read can ` + + "return; refusing to modify labels from a truncated label set", + ); + } + return result.project.labels.nodes.map((label) => asUuid(label.id)); } diff --git a/tests/unit/services/project-service.test.ts b/tests/unit/services/project-service.test.ts index 0c74e223..20a1595e 100644 --- a/tests/unit/services/project-service.test.ts +++ b/tests/unit/services/project-service.test.ts @@ -9,6 +9,7 @@ import { GetProjectDocument, GetProjectLabelIdsDocument, GetProjectWithReactionsDocument, + UpdateProjectDocument, } from "../../../src/gql/graphql.js"; import { archiveProject, @@ -354,7 +355,10 @@ describe("getProjectLabelIds", () => { const client = mockGqlClient({ project: { id: "proj-1", - labels: { nodes: [{ id: "label-1" }, { id: "label-2" }] }, + labels: { + nodes: [{ id: "label-1" }, { id: "label-2" }], + pageInfo: { hasNextPage: false }, + }, }, }); @@ -372,6 +376,115 @@ describe("getProjectLabelIds", () => { getProjectLabelIds(client, asUuid("nonexistent")), ).rejects.toThrow('Project with ID "nonexistent" not found'); }); + + it("throws instead of returning a truncated label set", async () => { + const client = mockGqlClient({ + project: { + id: "proj-1", + labels: { + nodes: [{ id: "label-1" }], + pageInfo: { hasNextPage: true }, + }, + }, + }); + + await expect(getProjectLabelIds(client, asUuid("proj-1"))).rejects.toThrow( + "refusing to modify labels from a truncated label set", + ); + }); +}); + +describe("project fragment connection bounds", () => { + // Linear's complexity estimator charges an unbounded connection at its + // default page size per parent row; unbounded connections in these + // fragments are what made default `projects list`/`read` exceed the + // 10000 budget (#276, #283). These assertions make a future tidy-up + // that unbounds them fail CI instead of shipping the regression. + // pageInfo itself prices at ~23 complexity per parent row, so the + // truncation signal lives only in the detail fragment (parent count of + // one) — selecting it in ProjectListFields would put the 100-row list + // back over budget. + const BOUNDED_CONNECTIONS: Array<[string, string, boolean]> = [ + ["ProjectListFields", "teams", false], + ["ProjectListFields", "labels", false], + ["ProjectDetailFields", "teams", true], + ["ProjectDetailFields", "labels", true], + ["ProjectDetailFields", "members", true], + ["ProjectDetailFields", "initiatives", true], + ["ProjectDetailWithDefaultConnectionsFields", "projectMilestones", true], + ]; + + it.each(BOUNDED_CONNECTIONS)( + "%s bounds %s with a literal first argument", + (fragmentName, connectionName, requiresPageInfo) => { + const fragment = getFragment(UpdateProjectDocument, fragmentName); + + const field = fragment.selectionSet.selections.find( + (selection) => + selection.kind === Kind.FIELD && + selection.name.value === connectionName, + ); + if (!field || field.kind !== Kind.FIELD) { + throw new Error(`${fragmentName} does not select ${connectionName}`); + } + + const firstArgument = field.arguments?.find( + (argument) => argument.name.value === "first", + ); + expect( + firstArgument?.value.kind, + `${fragmentName}.${connectionName} must carry a literal first: bound`, + ).toBe(Kind.INT); + + if (requiresPageInfo) { + const pageInfo = field.selectionSet?.selections.find( + (selection) => + selection.kind === Kind.FIELD && + selection.name.value === "pageInfo", + ); + expect( + pageInfo, + `${fragmentName}.${connectionName} must select pageInfo so consumers can detect truncation`, + ).toBeDefined(); + } + }, + ); + + it("bounds the lean label lookup and selects its truncation signal", () => { + const operation = GetProjectLabelIdsDocument.definitions.find( + (definition) => definition.kind === Kind.OPERATION_DEFINITION, + ); + if (!operation || operation.kind !== Kind.OPERATION_DEFINITION) { + throw new Error("GetProjectLabelIds operation not found"); + } + + const projectField = operation.selectionSet.selections.find( + (selection) => + selection.kind === Kind.FIELD && selection.name.value === "project", + ); + if (!projectField || projectField.kind !== Kind.FIELD) { + throw new Error("GetProjectLabelIds does not select project"); + } + + const labelsField = projectField.selectionSet?.selections.find( + (selection) => + selection.kind === Kind.FIELD && selection.name.value === "labels", + ); + if (!labelsField || labelsField.kind !== Kind.FIELD) { + throw new Error("GetProjectLabelIds does not select labels"); + } + + const firstArgument = labelsField.arguments?.find( + (argument) => argument.name.value === "first", + ); + expect(firstArgument?.value.kind).toBe(Kind.INT); + + const pageInfo = labelsField.selectionSet?.selections.find( + (selection) => + selection.kind === Kind.FIELD && selection.name.value === "pageInfo", + ); + expect(pageInfo).toBeDefined(); + }); }); describe("createProject", () => { From 9c942b3314bbba23a592f9e237ab169f1fb6b5b0 Mon Sep 17 00:00:00 2001 From: semantic-release-bot <semantic-release-bot@martynus.net> Date: Fri, 7 Aug 2026 06:47:23 +0000 Subject: [PATCH 34/34] chore(release): 2026.7.0-next.6 [skip ci] ## [2026.7.0-next.6](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.5...v2026.7.0-next.6) (2026-08-07) --- CHANGELOG.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72c2ad66..7a44c933 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [2026.7.0-next.6](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.5...v2026.7.0-next.6) (2026-08-07) + ## [2026.7.0-next.5](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.4...v2026.7.0-next.5) (2026-08-06) ## [2026.7.0-next.4](https://github.com/linearis-oss/linearis/compare/v2026.7.0-next.3...v2026.7.0-next.4) (2026-08-06) diff --git a/package-lock.json b/package-lock.json index b88fb907..b3383feb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "linearis", - "version": "2026.7.0-next.5", + "version": "2026.7.0-next.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "linearis", - "version": "2026.7.0-next.5", + "version": "2026.7.0-next.6", "license": "MIT", "dependencies": { "commander": "15.0.0", diff --git a/package.json b/package.json index cad565d9..8d713891 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linearis", - "version": "2026.7.0-next.5", + "version": "2026.7.0-next.6", "description": "CLI tool for Linear.app with JSON output, smart ID resolution, and optimized GraphQL queries. Designed for LLM agents and humans who prefer structured data.", "main": "dist/main.js", "type": "module",