diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1e4dee1..60281f5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -35,6 +35,17 @@ "npm: watch-tests" ], "problemMatcher": [] + }, + { + "label": "webpack: watch", + "type": "shell", + "command": "npm", + "args": [ + "run", + "watch" + ], // Assuming you have a "watch" script in your package.json + "isBackground": true, + "problemMatcher": "$tsc-watch" } ] } \ No newline at end of file diff --git a/README.md b/README.md index f83791c..24ae384 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,16 @@ This extension integrates the power of GitHub Copilot's GenAI capabilities into - **Docs**: This feature provides a comprehensive search functionality through the aem.live documentation. It's specifically tailored to help you find any information related to Edge Delivery Services swiftly and accurately. This aims to minimize the time spent on searching for specific documentation, thereby increasing your productivity. +- **Issues**: Get detailed descriptions of GitHub issues for your project, along with Copilot's suggestions for resolutions and improvements. This feature enables a more interactive and guided approach to issue resolution, leveraging AI to suggest potential fixes and optimizations. + ## Demo ![demo](https://raw.githubusercontent.com/adobe/aem-github-copilot/main/resources/demo.png) + +Please enter the block name you want to fetch \n\n Example: >@aem /collection block-name + + + diff --git a/package-lock.json b/package-lock.json index 637970d..5d8e7c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,17 +11,19 @@ "jsdom": "^24.1.0" }, "devDependencies": { + "@octokit/rest": "^20.1.1", "@types/jsdom": "^21.1.6", "@types/mocha": "^10.0.6", "@types/node": "^20.5.9", "@types/vscode": "1.90.0", - "@typescript-eslint/eslint-plugin": "^7.11.0", - "@typescript-eslint/parser": "^7.11.0", + "@typescript-eslint/eslint-plugin": "^7.16.1", + "@typescript-eslint/parser": "^7.16.1", "@vscode/prompt-tsx": "^0.2.3-alpha", "@vscode/test-cli": "^0.0.9", "@vscode/test-electron": "^2.4.0", "eslint": "^8.57.0", "run-script-os": "^1.1.6", + "simple-git": "^3.24.0", "ts-loader": "^9.5.1", "tslint": "^6.1.3", "typescript": "^5.5.2", @@ -361,6 +363,21 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -396,6 +413,161 @@ "node": ">= 8" } }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", + "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz", + "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz", + "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==", + "dev": true, + "dependencies": { + "@octokit/request": "^8.3.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.1.tgz", + "integrity": "sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", + "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.2.tgz", + "integrity": "sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz", + "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^9.0.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz", + "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.1.tgz", + "integrity": "sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==", + "dev": true, + "dependencies": { + "@octokit/core": "^5.0.2", + "@octokit/plugin-paginate-rest": "11.3.1", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "13.2.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -483,16 +655,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.0.tgz", - "integrity": "sha512-FX1X6AF0w8MdVFLSdqwqN/me2hyhuQg4ykN6ZpVhh1ij/80pTvDKclX1sZB9iqex8SjQfVhwMKs3JtnnMLzG9w==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz", + "integrity": "sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/type-utils": "7.13.0", - "@typescript-eslint/utils": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/type-utils": "7.16.1", + "@typescript-eslint/utils": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -516,15 +688,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.0.tgz", - "integrity": "sha512-EjMfl69KOS9awXXe83iRN7oIEXy9yYdqWfqdrFAYAAr6syP8eLEFI7ZE4939antx2mNgPRW/o1ybm2SFYkbTVA==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.1.tgz", + "integrity": "sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/typescript-estree": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/typescript-estree": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", "debug": "^4.3.4" }, "engines": { @@ -544,13 +716,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz", - "integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz", + "integrity": "sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0" + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -561,13 +733,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.0.tgz", - "integrity": "sha512-xMEtMzxq9eRkZy48XuxlBFzpVMDurUAfDu5Rz16GouAtXm0TaAoTFzqWUFPPuQYXI/CDaH/Bgx/fk/84t/Bc9A==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.1.tgz", + "integrity": "sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.0", - "@typescript-eslint/utils": "7.13.0", + "@typescript-eslint/typescript-estree": "7.16.1", + "@typescript-eslint/utils": "7.16.1", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -588,9 +760,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz", - "integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.1.tgz", + "integrity": "sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -601,13 +773,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz", - "integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz", + "integrity": "sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -638,9 +810,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -653,15 +825,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz", - "integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.1.tgz", + "integrity": "sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/typescript-estree": "7.13.0" + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/typescript-estree": "7.16.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -675,12 +847,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz", - "integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz", + "integrity": "sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/types": "7.16.1", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1138,6 +1310,12 @@ } ] }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1629,6 +1807,12 @@ "node": ">=0.4.0" } }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -3908,6 +4092,38 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-git": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.25.0.tgz", + "integrity": "sha512-KIY5sBnzc4yEcJXW7Tdv4viEz8KyG+nU0hay+DWZasvdFOYKeUZ6Xc25LUHHjw0tinPT7O1eY6pzX7pRT1K8rw==", + "dev": true, + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/simple-git/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4509,6 +4725,12 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true + }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", diff --git a/package.json b/package.json index fa02c2e..91f58f7 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,10 @@ { "name": "docs", "description": "Information About AEM Edge Delivery Services" + }, + { + "name": "issues", + "description": "Issues management for AEM Edge Delivery Services" } ] } @@ -69,12 +73,13 @@ "build": "webpack --mode production" }, "devDependencies": { + "@octokit/rest": "^20.1.1", "@types/jsdom": "^21.1.6", "@types/mocha": "^10.0.6", "@types/node": "^20.5.9", "@types/vscode": "1.90.0", - "@typescript-eslint/eslint-plugin": "^7.11.0", - "@typescript-eslint/parser": "^7.11.0", + "@typescript-eslint/eslint-plugin": "^7.16.1", + "@typescript-eslint/parser": "^7.16.1", "@vscode/prompt-tsx": "^0.2.3-alpha", "@vscode/test-cli": "^0.0.9", "@vscode/test-electron": "^2.4.0", @@ -84,9 +89,10 @@ "tslint": "^6.1.3", "typescript": "^5.5.2", "webpack": "^5.91.0", - "webpack-cli": "^5.1.4" + "webpack-cli": "^5.1.4", + "simple-git": "^3.24.0" }, "dependencies": { "jsdom": "^24.1.0" } -} +} \ No newline at end of file diff --git a/src/aem.commands.ts b/src/aem.commands.ts index 1b12e7f..6c27e08 100644 --- a/src/aem.commands.ts +++ b/src/aem.commands.ts @@ -4,4 +4,5 @@ export enum AEM_COMMANDS { // ENHANCE = "enhance", //TODO COLLECION = "collection", DOCS = "docs", + ISSUES = "issues", } \ No newline at end of file diff --git a/src/constants.ts b/src/constants.ts index 0662f27..fba3511 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,6 +6,7 @@ export const PROCESS_COPILOT_CREATE_CMD = "aem.createFiles"; export const PROCESS_COPILOT_CREATE_CMD_TITLE = "Create Block"; export const COPILOT_CREATE_CMD = "AEM block with base template"; + // Github Repository Details export const OWNER = 'adobe'; export const REPO = 'aem-block-collection'; @@ -17,6 +18,9 @@ export const MODEL_VENDOR: string = "copilot"; export const LANGUAGE_MODEL_ID: string = "gpt-3.5-turbo"; // Use faster model. Alternative is 'gpt-4', which is slower but more powerful export const MODEL_SELECTOR: vscode.LanguageModelChatSelector = { vendor: MODEL_VENDOR, family: LANGUAGE_MODEL_ID }; +// Issue Management Constants +export const FETCH_ISSUE_DETAIL_CMD = "Fetch Issue Details Command"; + export const GREETINGS = [ "Let me think how I can help you... 🤔", diff --git a/src/extension.ts b/src/extension.ts index 432e36f..3eaa595 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode"; -import { AEM_COMMANDS as commands } from "./aem.commands"; +import { AEM_COMMANDS, AEM_COMMANDS as commands } from "./aem.commands"; import { createCmdHandler, } from "./handlers/block.create"; @@ -8,11 +8,13 @@ import { handleDocsCommand } from "./handlers/block.docs"; import { AEM_COMMAND_ID, + FETCH_ISSUE_DETAIL_CMD, PROCESS_COPILOT_CREATE_CMD, } from "./constants"; import { createFolderAndFiles } from "./utils/fileHandler"; import { fetchBlock } from "./handlers/block.collections"; import { getRandomGreeting } from "./utils/helpers"; +import { handleIssueManagement } from "./handlers/issueManagement.handler"; interface IAemChatResult extends vscode.ChatResult { metadata: { @@ -37,6 +39,8 @@ export function activate(context: vscode.ExtensionContext) { cmdResult = await createCmdHandler(request, stream, token); } else if (request.command === commands.COLLECION) { cmdResult = await fetchBlock(request, stream, token, context); + } else if (request.command === commands.ISSUES) { + cmdResult = await handleIssueManagement(request, stream, token); } else { cmdResult = await handleDocsCommand(request, stream, token); } @@ -44,7 +48,11 @@ export function activate(context: vscode.ExtensionContext) { handleError(err, stream); } - return cmdResult.metadata; + return { + metadata: { + command: request.command || "", + }, + }; }; const aem = vscode.chat.createChatParticipant(AEM_COMMAND_ID, handler); @@ -76,6 +84,9 @@ export function activate(context: vscode.ExtensionContext) { await createFolderAndFiles(filesToCreate); } ), + vscode.commands.registerCommand(FETCH_ISSUE_DETAIL_CMD, async (githubIssue) => { + vscode.commands.executeCommand(`workbench.action.chat.open`, `@${AEM_COMMAND_ID} /${commands.ISSUES} fetch me details of issue #${githubIssue.number}`); + }) ); } diff --git a/src/handlers/block.collections.ts b/src/handlers/block.collections.ts index 0f08e91..b44e4fd 100644 --- a/src/handlers/block.collections.ts +++ b/src/handlers/block.collections.ts @@ -21,6 +21,7 @@ export async function fetchBlock( `List of available blocks: \n\n - ${blockList?.join("\n - ")}` : `Block not found in collection \n here is the list of available blocks: \n\n - ${blockList?.join("\n - ")}`; stream.markdown(vscode.l10n.t(message)); + stream.markdown(vscode.l10n.t(`\n\nPlease enter the block name you want to fetch \n\n >***@aem /collection block-name***`)); } else { try { diff --git a/src/handlers/block.create.ts b/src/handlers/block.create.ts index 07ea39d..d148896 100644 --- a/src/handlers/block.create.ts +++ b/src/handlers/block.create.ts @@ -15,7 +15,7 @@ export async function createCmdHandler( const promptProps = { userQuery: request.prompt, }; - const chatResponse = await getChatResponse(CreateBlockPrompt, promptProps, token, ); + const chatResponse = await getChatResponse(CreateBlockPrompt, promptProps, token); let resultJsonStr = ""; for await (const fragment of chatResponse.text) { resultJsonStr += fragment; diff --git a/src/handlers/block.docs.ts b/src/handlers/block.docs.ts index d984ea3..9784059 100644 --- a/src/handlers/block.docs.ts +++ b/src/handlers/block.docs.ts @@ -3,7 +3,7 @@ import { AEM_COMMANDS as commands } from "../aem.commands"; import { MODEL_SELECTOR } from '../constants'; import { DocsPrompt } from '../prompts/block.docs'; import { getChatResponse } from '../utils/helpers'; -import { DocsPromptProps } from '../interfaces/promptInterfaces'; +import { DocsPromptProps } from '../interfaces/prompt.Interfaces'; const INDEX_URL: string = "https://www.aem.live/docpages-index.json"; diff --git a/src/handlers/block.info.ts b/src/handlers/block.info.ts index 5209ad7..6a5b02b 100644 --- a/src/handlers/block.info.ts +++ b/src/handlers/block.info.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import { AEM_COMMANDS as commands } from "../aem.commands"; import { AEMInfoPrompt } from "../prompts/eds.info.prompt"; import { getChatResponse } from "../utils/helpers"; -import { PromptProps } from "../interfaces/promptInterfaces"; +import { PromptProps } from "../interfaces/prompt.Interfaces"; export async function infoCmdHandler( request: vscode.ChatRequest, diff --git a/src/handlers/issueManagement.handler.ts b/src/handlers/issueManagement.handler.ts new file mode 100644 index 0000000..fa6b31a --- /dev/null +++ b/src/handlers/issueManagement.handler.ts @@ -0,0 +1,140 @@ +import * as vscode from "vscode"; +import { AEM_COMMANDS as commands } from "../aem.commands"; +import { Comment, Issue } from "../interfaces/issueManagement.interfaces"; +import { fetchAllIssues, fetchIssueDetailsByNumber, fetchLatestIssueDetails, getGitHubClient } from "../utils/github.helper"; +import { FETCH_ISSUE_DETAIL_CMD } from "../constants"; +import { IssuesManagePrompt } from "../prompts/issueManagement.prompt"; +import { IssuesManagePromptProps } from "../interfaces/prompt.Interfaces"; +import { getChatResponse } from "../utils/helpers"; + +// Extracts owner and repo name from the workspace's Git configuration +async function extractRepoDetailsFromWorkspace(): Promise<{ owner: string; repoName: string } | null> { + const gitExtension = vscode.extensions.getExtension('vscode.git')?.exports; + if (!gitExtension) { + vscode.window.showErrorMessage('Unable to load Git extension'); + return null; + } + + const api = gitExtension.getAPI(1); + if (api.repositories.length === 0) { + vscode.window.showInformationMessage('No Git repositories found'); + return null; + } + + const repo = api.repositories[0]; + const remotes = repo.state.remotes; + if (remotes.length === 0) { + vscode.window.showInformationMessage('No remotes found'); + return null; + } + + const remoteUrl = remotes[0].fetchUrl; + const match = remoteUrl?.match(/github\.com[:/](.+?)\/(.+?)(?:\.git)?$/); + if (!match) { + vscode.window.showErrorMessage('Unable to parse GitHub repository URL'); + return null; + } + + return { owner: match[1], repoName: match[2] }; +} + + +function streamIssueDetails( + stream: vscode.ChatResponseStream, + issue: any, + comments: Comment[] +) { + stream.progress(`Issue "${issue.title}" loaded.`); + stream.markdown(`Issue: **${issue.title}**\n\n`); + stream.markdown(issue.body?.replaceAll("\n", "\n> ") + ""); + if (comments?.length > 0) { + stream.markdown("\n\n_Comments_\n"); + comments?.map((comment) => + stream.markdown(`\n> ${comment.body?.replaceAll("\n", "\n> ") + ""}\n`) + ); + } + stream.markdown("\n\n----\n\n"); +} + +async function streamCopilotResponse(stream: vscode.ChatResponseStream, issueDetails: Issue, promptProps: IssuesManagePromptProps, token: vscode.CancellationToken) { + stream.progress(`Copilot suggestion....`); + try { + const issueDetailsStr = `The issue to work on has the title: "${issueDetails?.title}" and the description: ${issueDetails?.body}. Use that information to give better answer for the following user query.` + + (issueDetails?.comments && issueDetails?.comments?.length > 0 + ? `Do also regard the comments: ${issueDetails?.comments + ?.map((comment) => comment.body) + .join("\n\n") + "" + }` + : ""); + promptProps.issueDetails = issueDetailsStr; + const chatResponse = await getChatResponse(IssuesManagePrompt, promptProps, token,); + for await (const fragment of chatResponse.text) { + stream.markdown(fragment); + } + } catch (error) { + stream.markdown("Some Network issues. Please try again.."); + } +} + +// Main handler for issue management +export async function handleIssueManagement(request: vscode.ChatRequest, stream: vscode.ChatResponseStream, token: vscode.CancellationToken): Promise<{ metadata: { command: string } }> { + const userQuery = request.prompt.toLowerCase(); + const workspaceDetails = await extractRepoDetailsFromWorkspace(); + if (!workspaceDetails) { + stream.markdown("Repository details not found."); + return { metadata: { command: commands.ISSUES } }; + } + + let promptProps: IssuesManagePromptProps = { + userQuery: request.prompt, + issueDetails: "", + }; + + const octokit = await getGitHubClient(); + + // Determine action based on user query + if (userQuery.includes("latest issue")) { + const issueDetails = await fetchLatestIssueDetails(workspaceDetails.owner, workspaceDetails.repoName, octokit); + if (!issueDetails) { + stream.markdown("Latest issue details not found."); + return { metadata: { command: commands.ISSUES } }; + } + streamIssueDetails(stream, issueDetails, issueDetails.comments); + await streamCopilotResponse(stream, issueDetails, promptProps, token); + } else if (userQuery.match(/issue #(\d+)/)) { + const match = userQuery.match(/issue #(\d+)/); + const issueNumber = match ? parseInt(match[1]) : null; + if (!issueNumber) { + stream.markdown("Issue number not found."); + return { metadata: { command: commands.ISSUES } }; + } + const issueDetails = await fetchIssueDetailsByNumber(workspaceDetails.owner, workspaceDetails.repoName, issueNumber, octokit); + if (!issueDetails) { + stream.markdown(`Details for issue #${issueNumber} not found.`); + return { metadata: { command: commands.ISSUES } }; + } + streamIssueDetails(stream, issueDetails, issueDetails.comments); + await streamCopilotResponse(stream, issueDetails, promptProps, token); + } else if (userQuery.includes("list")) { + const issuesList = await fetchAllIssues(workspaceDetails.owner, workspaceDetails.repoName, octokit, 5); + if (!issuesList || issuesList.length === 0) { + stream.markdown("No issues found."); + return { metadata: { command: commands.ISSUES } }; + } + stream.markdown("Here are the latest issues:\n"); + issuesList.forEach((issue: any) => { + stream.button({ + command: FETCH_ISSUE_DETAIL_CMD, + title: `${issue.number}: ${issue.title}`, + tooltip: vscode.l10n.t(`Fetch details for Issue #${issue.number}`), + arguments: [issue], + + }); + }); + } else { + stream.markdown("I'm not sure how to help with that. You can ask for the 'latest issue', 'list all issues', or about a specific 'issue #number'."); + return { metadata: { command: commands.ISSUES } }; + } + + return { metadata: { command: commands.ISSUES } }; +} diff --git a/src/interfaces/fileinterface.ts b/src/interfaces/file.interfaces.ts similarity index 100% rename from src/interfaces/fileinterface.ts rename to src/interfaces/file.interfaces.ts diff --git a/src/interfaces/issueManagement.interfaces.ts b/src/interfaces/issueManagement.interfaces.ts new file mode 100644 index 0000000..2e5e634 --- /dev/null +++ b/src/interfaces/issueManagement.interfaces.ts @@ -0,0 +1,11 @@ +export interface Comment { + id: number; + url: string; + body?: string | undefined; +} + +export interface Issue { + title: string; + body: string; + comments: Comment[]; +} \ No newline at end of file diff --git a/src/interfaces/promptInterfaces.ts b/src/interfaces/prompt.Interfaces.ts similarity index 81% rename from src/interfaces/promptInterfaces.ts rename to src/interfaces/prompt.Interfaces.ts index d5bd536..3d25dbb 100644 --- a/src/interfaces/promptInterfaces.ts +++ b/src/interfaces/prompt.Interfaces.ts @@ -26,4 +26,11 @@ export interface DocsPromptProps extends PromptProps { */ export interface CreatePromptState { projectStyleCSS: string; +} + +/** + * Prompt state for the issues manage prompt + */ +export interface IssuesManagePromptProps extends PromptProps { + issueDetails: string; } \ No newline at end of file diff --git a/src/prompts/block.docs.tsx b/src/prompts/block.docs.tsx index d42d43d..12557f2 100644 --- a/src/prompts/block.docs.tsx +++ b/src/prompts/block.docs.tsx @@ -5,7 +5,7 @@ import { } from '@vscode/prompt-tsx'; import * as prompts from "./templates/default"; -import { DocsPromptProps } from '../interfaces/promptInterfaces'; +import { DocsPromptProps } from '../interfaces/prompt.Interfaces'; export class DocsPrompt extends PromptElement { diff --git a/src/prompts/create.block.tsx b/src/prompts/create.block.tsx index 5e20c58..698f6d9 100644 --- a/src/prompts/create.block.tsx +++ b/src/prompts/create.block.tsx @@ -7,7 +7,7 @@ import { import * as prompts from "./templates/create.block"; import { getProjectLevelStyles } from '../utils/helpers'; -import { CreatePromptState, PromptProps } from '../interfaces/promptInterfaces'; +import { CreatePromptState, PromptProps } from '../interfaces/prompt.Interfaces'; export class CreateBlockPrompt extends PromptElement { diff --git a/src/prompts/eds.info.prompt.tsx b/src/prompts/eds.info.prompt.tsx index a26b8ec..e84b570 100644 --- a/src/prompts/eds.info.prompt.tsx +++ b/src/prompts/eds.info.prompt.tsx @@ -4,8 +4,8 @@ import { UserMessage, } from '@vscode/prompt-tsx'; -import * as prompts from "./templates/eds.info.prompt"; -import { PromptProps } from '../interfaces/promptInterfaces'; +import * as prompts from "./templates/eds.info"; +import { PromptProps } from '../interfaces/prompt.Interfaces'; export class AEMInfoPrompt extends PromptElement { diff --git a/src/prompts/issueManagement.prompt.tsx b/src/prompts/issueManagement.prompt.tsx new file mode 100644 index 0000000..3970bc3 --- /dev/null +++ b/src/prompts/issueManagement.prompt.tsx @@ -0,0 +1,27 @@ +import { + PromptElement, + PromptSizing, + UserMessage, +} from '@vscode/prompt-tsx'; + +import { IssuesManagePromptProps } from '../interfaces/prompt.Interfaces'; + +export const SYSTEM_MESSAGE = `You are a software product owner and you help your developers providing additional information +for working on current software development task from github issue details.`; + +export class IssuesManagePrompt extends PromptElement { + + render(state: void, sizing: PromptSizing) { + return ( + <> + {SYSTEM_MESSAGE} + + Here are the Github issue details
+ {this.props.issueDetails}
+ Explain the issue details to the developer and probably provide some additional information.
+ Provide the response in nice markdown format to the user. +
+ + ); + } +} \ No newline at end of file diff --git a/src/prompts/templates/eds.info.prompt.ts b/src/prompts/templates/eds.info.ts similarity index 100% rename from src/prompts/templates/eds.info.prompt.ts rename to src/prompts/templates/eds.info.ts diff --git a/src/utils/github.helper.ts b/src/utils/github.helper.ts new file mode 100644 index 0000000..73d0cd6 --- /dev/null +++ b/src/utils/github.helper.ts @@ -0,0 +1,88 @@ +import { Octokit } from "@octokit/rest"; +import * as vscode from "vscode"; +import { Comment, Issue } from "../interfaces/issueManagement.interfaces"; + + +// Utility function to get GitHub Octokit client +export async function getGitHubClient(): Promise { + const session = await vscode.authentication.getSession("github", ["repo"], { createIfNone: true }); + return new Octokit({ auth: session.accessToken }); +} + + +// Fetches the latest issue details including comments +export async function fetchLatestIssueDetails(owner: string, repoName: string, octokit: Octokit): Promise< Issue| null> { + try { + const issues = await octokit.issues.listForRepo({ + owner, + repo: repoName, + state: 'open', + per_page: 1, + sort: 'created', + direction: 'desc', + }); + + if (issues.data.length === 0) { + vscode.window.showInformationMessage('No open issues found in the repository'); + return null; + } + + const latestIssue = issues.data[0]; + const commentsResponse = await octokit.issues.listComments({ + owner, + repo: repoName, + issue_number: latestIssue.number, + }); + + return { + title: latestIssue.title, + body: latestIssue.body || '', + comments: commentsResponse.data, + }; + } catch (error) { + vscode.window.showErrorMessage(`Error fetching issue details: ${error}`); + return null; + } +} + +export async function fetchIssueDetailsByNumber(owner: string, repoName: string, issueNumber: number, octokit: Octokit): Promise< Issue | null> { + try { + const issue = await octokit.issues.get({ + owner, + repo: repoName, + issue_number: issueNumber, + }); + + const commentsResponse = await octokit.issues.listComments({ + owner, + repo: repoName, + issue_number: issueNumber, + }); + + return { + title: issue.data.title, + body: issue.data.body || '', + comments: commentsResponse.data, + }; + } catch (error) { + vscode.window.showErrorMessage(`Error fetching issue details: ${error}`); + return null; + } +} + +export async function fetchAllIssues(owner: string, repoName: string, octokit: Octokit, top_n: number): Promise { + try { + const issues = await octokit.issues.listForRepo({ + owner, + repo: repoName, + state: 'open', + per_page: top_n, // Use top_n for per_page + }); + + // If the API returns more issues than top_n, slice the array to return only top_n issues + return issues.data.length > top_n ? issues.data.slice(0, top_n) : issues.data; + } catch (error) { + vscode.window.showErrorMessage(`Error fetching issue details: ${error}`); + return null; + } +} \ No newline at end of file diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 2ea35bc..0f5765b 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -3,8 +3,8 @@ import { AEM_BLOCK_COLLECTION_URL, GREETINGS, MODEL_SELECTOR } from "../constant import { JSDOM } from 'jsdom'; import { PromptElementCtor, renderPrompt } from "@vscode/prompt-tsx"; -import { PromptProps } from "../interfaces/promptInterfaces"; -import { File } from "../interfaces/fileinterface"; +import { PromptProps } from "../interfaces/prompt.Interfaces"; +import { File } from "../interfaces/file.interfaces"; /** * Parses a string into a JSON object containing file information using regular expressions,