From 9ffcdbdb3b6f1ab6c86d1460508a49d737d0f190 Mon Sep 17 00:00:00 2001 From: Jethro Yu Date: Tue, 1 Apr 2025 15:14:09 +0800 Subject: [PATCH 01/10] refactor(commitlint): update commitlint configuration and prompts for improved clarity and consistency The commitlint configuration and prompts have been refactored to enhance clarity and maintain consistency throughout the codebase. The type assertion for commitLintConfig is updated to use 'as any' for better type handling. Additionally, formatting adjustments are made in the prompts to ensure proper readability and alignment with the defined conventions. These changes aim to streamline the commit message generation process and improve overall code maintainability. --- src/modules/commitlint/config.ts | 2 +- src/modules/commitlint/prompts.ts | 87 +++++++++++------------- src/modules/commitlint/pwd-commitlint.ts | 2 +- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/src/modules/commitlint/config.ts b/src/modules/commitlint/config.ts index 1c509f8..96a6f16 100644 --- a/src/modules/commitlint/config.ts +++ b/src/modules/commitlint/config.ts @@ -53,7 +53,7 @@ export const configureCommitlintIntegration = async (force = false) => { spin.start('Generating consistency with given @commitlint rules'); - const prompts = inferPromptsFromCommitlintConfig(commitLintConfig); + const prompts = inferPromptsFromCommitlintConfig(commitLintConfig as any); const consistencyPrompts = commitlintPrompts.GEN_COMMITLINT_CONSISTENCY_PROMPT(prompts); diff --git a/src/modules/commitlint/prompts.ts b/src/modules/commitlint/prompts.ts index 445e09d..8dd81c5 100644 --- a/src/modules/commitlint/prompts.ts +++ b/src/modules/commitlint/prompts.ts @@ -56,30 +56,28 @@ const llmReadableRules: { blankline: (key, applicable) => `There should ${applicable} be a blank line at the beginning of the ${key}.`, caseRule: (key, applicable, value: string | Array) => - `The ${key} should ${applicable} be in ${ - Array.isArray(value) - ? `one of the following case: + `The ${key} should ${applicable} be in ${Array.isArray(value) + ? `one of the following case: - ${value.join('\n - ')}.` - : `${value} case.` + : `${value} case.` }`, emptyRule: (key, applicable) => `The ${key} should ${applicable} be empty.`, enumRule: (key, applicable, value: string | Array) => - `The ${key} should ${applicable} be one of the following values: + `The ${key} should ${applicable} be one of the following values: - ${Array.isArray(value) ? value.join('\n - ') : value}.`, enumTypeRule: (key, applicable, value: string | Array, prompt) => - `The ${key} should ${applicable} be one of the following values: - - ${ - Array.isArray(value) + `The ${key} should ${applicable} be one of the following values: + - ${Array.isArray(value) ? value - .map((v) => { - const description = getTypeRuleExtraDescription(v, prompt); - if (description) { - return `${v} (${description})`; - } else return v; - }) - .join('\n - ') + .map((v) => { + const description = getTypeRuleExtraDescription(v, prompt); + if (description) { + return `${v} (${description})`; + } else return v; + }) + .join('\n - ') : value - }.`, + }.`, fullStopRule: (key, applicable, value: string) => `The ${key} should ${applicable} end with '${value}'.`, maxLengthRule: (key, applicable, value: string) => @@ -216,15 +214,15 @@ const STRUCTURE_OF_COMMIT = config.OCO_OMIT_SCOPE const GEN_COMMITLINT_CONSISTENCY_PROMPT = ( prompts: string[] ): OpenAI.Chat.Completions.ChatCompletionMessageParam[] => [ - { - role: 'system', - content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature. + { + role: 'system', + content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature. Here are the specific requirements and conventions that should be strictly followed: Commit Message Conventions: - The commit message consists of three parts: Header, Body, and Footer. -- Header: +- Header: - Format: ${config.OCO_OMIT_SCOPE ? '`: `' : '`(): `'} - ${prompts.join('\n- ')} @@ -240,7 +238,7 @@ JSON Output Format: "commitDescription": "" } \`\`\` -- The "commitDescription" should not include the commit message’s header, only the description. +- The "commitDescription" should not include the commit message's header, only the description. - Description should not be more than 74 characters. Additional Details: @@ -248,9 +246,9 @@ Additional Details: - Allowing the server to listen on a port specified through the environment variable is considered a new feature. Example Git Diff is to follow:` - }, - INIT_DIFF_PROMPT -]; + }, + INIT_DIFF_PROMPT + ]; /** * Prompt to have LLM generate a message using @commitlint rules. @@ -264,30 +262,25 @@ const INIT_MAIN_PROMPT = ( prompts: string[] ): OpenAI.Chat.Completions.ChatCompletionMessageParam => ({ role: 'system', - content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes ${ - config.OCO_WHY ? 'and WHY the changes were done' : '' - }. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. -${ - config.OCO_EMOJI - ? 'Use GitMoji convention to preface the commit.' - : 'Do not preface the commit with anything.' -} -${ - config.OCO_DESCRIPTION - ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.' - : "Don't add any descriptions to the commit, only commit message." -} + content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes ${config.OCO_WHY ? 'and WHY the changes were done' : '' + }. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. +${config.OCO_EMOJI + ? 'Use GitMoji convention to preface the commit.' + : 'Do not preface the commit with anything.' + } +${config.OCO_DESCRIPTION + ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.' + : "Don't add any descriptions to the commit, only commit message." + } Use the present tense. Use ${language} to answer. -${ - config.OCO_ONE_LINE_COMMIT - ? 'Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change.' - : '' -} -${ - config.OCO_OMIT_SCOPE - ? 'Do not include a scope in the commit message format. Use the format: : ' - : '' -} +${config.OCO_ONE_LINE_COMMIT + ? 'Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change.' + : '' + } +${config.OCO_OMIT_SCOPE + ? 'Do not include a scope in the commit message format. Use the format: : ' + : '' + } You will strictly follow the following conventions to generate the content of the commit message: - ${prompts.join('\n- ')} diff --git a/src/modules/commitlint/pwd-commitlint.ts b/src/modules/commitlint/pwd-commitlint.ts index e01a4a6..1a6d8ed 100644 --- a/src/modules/commitlint/pwd-commitlint.ts +++ b/src/modules/commitlint/pwd-commitlint.ts @@ -60,7 +60,7 @@ export const getCommitLintPWDConfig = * ES Module (commitlint@v19.x.x. <= ) * Directory import is not supported in ES Module resolution, so import the file directly */ - modulePath = await findModulePath('@commitlint/load/lib/load.js'); + modulePath = findModulePath('@commitlint/load/lib/load.js'); load = (await import(modulePath)).default; break; } From 5381c5e18bf440e27bf9414b9b8ee0ec9686268e Mon Sep 17 00:00:00 2001 From: Jethro Yu Date: Tue, 15 Apr 2025 14:14:54 +0800 Subject: [PATCH 02/10] chore(package.json): add rimraf as a development dependency for improved file management Including rimraf in the project allows for easier and more efficient removal of files and directories, enhancing the development workflow, especially for tasks like cleaning up build artifacts. --- package-lock.json | 430 ++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + 2 files changed, 379 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index 930dae3..a7f82d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "eslint": "^9.24.0", "jest": "^29.7.0", "prettier": "^2.8.4", + "rimraf": "^6.0.1", "ts-jest": "^29.1.2", "ts-node": "^10.9.1", "typescript": "^4.9.3" @@ -1225,6 +1226,109 @@ "node": ">=18" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2255,17 +2359,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", - "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/type-utils": "8.29.1", - "@typescript-eslint/utils": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2285,16 +2389,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", - "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4" }, "engines": { @@ -2310,14 +2414,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", - "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1" + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2328,14 +2432,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", - "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -2352,9 +2456,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", - "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", "dev": true, "license": "MIT", "engines": { @@ -2366,14 +2470,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", - "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2393,16 +2497,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", - "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1" + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2417,13 +2521,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", - "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/types": "8.30.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3476,6 +3580,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -3493,9 +3604,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.136", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.136.tgz", - "integrity": "sha512-kL4+wUTD7RSA5FHx5YwWtjDnEEkIIikFgWHR4P6fqjw1PPLlqYkxeOb++wAauAssat0YClCy8Y3C5SxgSkjibQ==", + "version": "1.5.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", + "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", "dev": true, "license": "ISC" }, @@ -4580,6 +4691,36 @@ } } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", @@ -5272,6 +5413,22 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", + "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", @@ -6980,6 +7137,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7117,9 +7284,9 @@ } }, "node_modules/openai": { - "version": "4.93.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.93.0.tgz", - "integrity": "sha512-2kONcISbThKLfm7T9paVzg+QCE1FOZtNMMUfXyXckUAoXRRS/mTP89JSDHPMp8uM5s0bz28RISbvQjArD6mgUQ==", + "version": "4.94.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.94.0.tgz", + "integrity": "sha512-WVmr9HWcwfouLJ7R3UHd2A93ClezTPuJljQxkCYQAL15Sjyt+FBNoqEz5MHSdH/ebQrVyvRhFyn/bvdqtSPyIA==", "license": "Apache-2.0", "dependencies": { "@types/node": "^18.11.18", @@ -7278,6 +7445,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7346,6 +7520,33 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7726,6 +7927,66 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-async": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", @@ -7972,6 +8233,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -7984,6 +8261,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -8171,9 +8462,9 @@ } }, "node_modules/ts-jest": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.1.tgz", - "integrity": "sha512-FT2PIRtZABwl6+ZCry8IY7JZ3xMuppsEV9qFVHOVe8jDzggwUZ9TsM4chyJxL9yi6LvkqcZYU3LmapEE454zBQ==", + "version": "29.3.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.2.tgz", + "integrity": "sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug==", "dev": true, "license": "MIT", "dependencies": { @@ -8185,7 +8476,7 @@ "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.7.1", - "type-fest": "^4.38.0", + "type-fest": "^4.39.1", "yargs-parser": "^21.1.1" }, "bin": { @@ -8522,6 +8813,41 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", diff --git a/package.json b/package.json index 0890730..6c47a6b 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "eslint": "^9.24.0", "jest": "^29.7.0", "prettier": "^2.8.4", + "rimraf": "^6.0.1", "ts-jest": "^29.1.2", "ts-node": "^10.9.1", "typescript": "^4.9.3" From a52589e9fe6ca1770470922ca3da705b0e8a77d2 Mon Sep 17 00:00:00 2001 From: Jethro Yu Date: Tue, 15 Apr 2025 14:54:40 +0800 Subject: [PATCH 03/10] fix(test): Stabilize Jest ESM configuration for CI Resolves persistent `ReferenceError: exports is not defined` errors encountered during unit tests (`unit-test (20.x)` job) in the GitHub Actions CI environment. These errors occurred specifically when importing `cli-testing-library` in the global Jest setup file (`test/jest-setup.ts`), despite tests passing locally with the same Node.js version (v20.19.0). After iterative testing, the following Jest configuration combination was identified as necessary to ensure consistent ESM handling and test success in both local and CI environments: - Set preset to `ts-jest/presets/default-esm` for stricter ESM rules. - Configured `transformIgnorePatterns` to ensure Jest transforms specific ESM dependencies (`cli-testing-library`, `@clack`, `cleye`) within `node_modules`. - Expanded the `transform` pattern (`^.+\\.(ts|tsx|js|jsx|mjs)$`) to explicitly cover various script types handled by `ts-jest`. - Included explicit `tsconfig` overrides (`module: 'ESNext'`, `target: 'ES2022'`) within the `ts-jest` transform options to resolve potential environment discrepancies. - Retained `moduleNameMapper` for `.js` imports for reliable module resolution. - Ensured `cli-testing-library` imports remain in the global setup (`test/jest-setup.ts`). - Removed test cache clearing from the `test:unit` script in `package.json`. This configuration now passes reliably across environments. --- .gitignore | 3 ++- jest.config.ts | 21 +++++++++++++++++---- test/jest-setup.ts | 6 +++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index e20df5e..1e5999b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ uncaughtExceptions.log src/*.json .idea test.ts -notes.md \ No newline at end of file +notes.md +.nvmrc \ No newline at end of file diff --git a/jest.config.ts b/jest.config.ts index ee0d397..e100760 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -9,19 +9,32 @@ const config: Config = { testTimeout: 100_000, coverageProvider: 'v8', moduleDirectories: ['node_modules', 'src'], - preset: 'ts-jest/presets/js-with-ts-esm', + preset: 'ts-jest/presets/default-esm', setupFilesAfterEnv: ['/test/jest-setup.ts'], testEnvironment: 'node', testRegex: ['.*\\.test\\.ts$'], - transformIgnorePatterns: ['node_modules/(?!cli-testing-library)'], + transformIgnorePatterns: [ + 'node_modules/(?!(cli-testing-library|@clack|cleye)/.*)' + ], transform: { - '^.+\\.(ts|tsx)$': [ + '^.+\\.(ts|tsx|js|jsx|mjs)$': [ 'ts-jest', { diagnostics: false, - useESM: true + useESM: true, + tsconfig: { + module: 'ESNext', + target: 'ES2022' + } } ] + }, + // Fix Haste module naming collision + modulePathIgnorePatterns: [ + '/test/e2e/prompt-module/data/' + ], + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1' } }; diff --git a/test/jest-setup.ts b/test/jest-setup.ts index 29280f7..cd7b699 100644 --- a/test/jest-setup.ts +++ b/test/jest-setup.ts @@ -1,10 +1,10 @@ -import 'cli-testing-library/extend-expect' -import { configure } from 'cli-testing-library' import { jest } from '@jest/globals'; +import 'cli-testing-library/extend-expect'; +import { configure } from 'cli-testing-library'; global.jest = jest; /** * Adjusted the wait time for waitFor/findByText to 2000ms, because the default 1000ms makes the test results flaky */ -configure({ asyncUtilTimeout: 2000 }) +configure({ asyncUtilTimeout: 2000 }); From 0ebff3b97454871e2c9282cbd3582d333b67531c Mon Sep 17 00:00:00 2001 From: Jethro Yu Date: Fri, 25 Apr 2025 18:39:04 +0800 Subject: [PATCH 04/10] fix(removeContentTags): keep newlines to preserve formatting The space normalization logic is updated to replace only multiple spaces and tabs with a single space, while preserving newlines. This change ensures that the formatting of the content is maintained, especially when dealing with empty line requirements and max line length. --- src/utils/removeContentTags.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/removeContentTags.ts b/src/utils/removeContentTags.ts index d478434..c665bf7 100644 --- a/src/utils/removeContentTags.ts +++ b/src/utils/removeContentTags.ts @@ -43,9 +43,9 @@ export function removeContentTags(content: result += content[i]; } } - - // Normalize spaces (replace multiple spaces with a single space) - result = result.replace(/\s+/g, ' ').trim(); - + + // Normalize multiple spaces/tabs into a single space (preserves newlines), then trim. + result = result.replace(/[ \t]+/g, ' ').trim(); + return result as unknown as T; } From da2742edb18116aa9b081576f22ee4dd0647d993 Mon Sep 17 00:00:00 2001 From: Jethro Yu Date: Tue, 15 Apr 2025 14:14:54 +0800 Subject: [PATCH 05/10] chore(package.json): add rimraf as a development dependency for improved file management Including rimraf in the project allows for easier and more efficient removal of files and directories, enhancing the development workflow, especially for tasks like cleaning up build artifacts. --- package-lock.json | 430 ++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + 2 files changed, 379 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index 930dae3..a7f82d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "eslint": "^9.24.0", "jest": "^29.7.0", "prettier": "^2.8.4", + "rimraf": "^6.0.1", "ts-jest": "^29.1.2", "ts-node": "^10.9.1", "typescript": "^4.9.3" @@ -1225,6 +1226,109 @@ "node": ">=18" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2255,17 +2359,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", - "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/type-utils": "8.29.1", - "@typescript-eslint/utils": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2285,16 +2389,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", - "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4" }, "engines": { @@ -2310,14 +2414,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", - "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1" + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2328,14 +2432,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", - "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -2352,9 +2456,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", - "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", "dev": true, "license": "MIT", "engines": { @@ -2366,14 +2470,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", - "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2393,16 +2497,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", - "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1" + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2417,13 +2521,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", - "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/types": "8.30.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3476,6 +3580,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -3493,9 +3604,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.136", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.136.tgz", - "integrity": "sha512-kL4+wUTD7RSA5FHx5YwWtjDnEEkIIikFgWHR4P6fqjw1PPLlqYkxeOb++wAauAssat0YClCy8Y3C5SxgSkjibQ==", + "version": "1.5.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", + "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", "dev": true, "license": "ISC" }, @@ -4580,6 +4691,36 @@ } } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", @@ -5272,6 +5413,22 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", + "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", @@ -6980,6 +7137,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7117,9 +7284,9 @@ } }, "node_modules/openai": { - "version": "4.93.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.93.0.tgz", - "integrity": "sha512-2kONcISbThKLfm7T9paVzg+QCE1FOZtNMMUfXyXckUAoXRRS/mTP89JSDHPMp8uM5s0bz28RISbvQjArD6mgUQ==", + "version": "4.94.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.94.0.tgz", + "integrity": "sha512-WVmr9HWcwfouLJ7R3UHd2A93ClezTPuJljQxkCYQAL15Sjyt+FBNoqEz5MHSdH/ebQrVyvRhFyn/bvdqtSPyIA==", "license": "Apache-2.0", "dependencies": { "@types/node": "^18.11.18", @@ -7278,6 +7445,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7346,6 +7520,33 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7726,6 +7927,66 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-async": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", @@ -7972,6 +8233,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -7984,6 +8261,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -8171,9 +8462,9 @@ } }, "node_modules/ts-jest": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.1.tgz", - "integrity": "sha512-FT2PIRtZABwl6+ZCry8IY7JZ3xMuppsEV9qFVHOVe8jDzggwUZ9TsM4chyJxL9yi6LvkqcZYU3LmapEE454zBQ==", + "version": "29.3.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.2.tgz", + "integrity": "sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug==", "dev": true, "license": "MIT", "dependencies": { @@ -8185,7 +8476,7 @@ "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.7.1", - "type-fest": "^4.38.0", + "type-fest": "^4.39.1", "yargs-parser": "^21.1.1" }, "bin": { @@ -8522,6 +8813,41 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", diff --git a/package.json b/package.json index 0890730..6c47a6b 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "eslint": "^9.24.0", "jest": "^29.7.0", "prettier": "^2.8.4", + "rimraf": "^6.0.1", "ts-jest": "^29.1.2", "ts-node": "^10.9.1", "typescript": "^4.9.3" From 2726e51c2a95ebc5ed30b82800b38fa5bd75de2f Mon Sep 17 00:00:00 2001 From: Jethro Yu Date: Tue, 15 Apr 2025 14:54:40 +0800 Subject: [PATCH 06/10] fix(test): Stabilize Jest ESM configuration for CI Resolves persistent `ReferenceError: exports is not defined` errors encountered during unit tests (`unit-test (20.x)` job) in the GitHub Actions CI environment. These errors occurred specifically when importing `cli-testing-library` in the global Jest setup file (`test/jest-setup.ts`), despite tests passing locally with the same Node.js version (v20.19.0). After iterative testing, the following Jest configuration combination was identified as necessary to ensure consistent ESM handling and test success in both local and CI environments: - Set preset to `ts-jest/presets/default-esm` for stricter ESM rules. - Configured `transformIgnorePatterns` to ensure Jest transforms specific ESM dependencies (`cli-testing-library`, `@clack`, `cleye`) within `node_modules`. - Expanded the `transform` pattern (`^.+\\.(ts|tsx|js|jsx|mjs)$`) to explicitly cover various script types handled by `ts-jest`. - Included explicit `tsconfig` overrides (`module: 'ESNext'`, `target: 'ES2022'`) within the `ts-jest` transform options to resolve potential environment discrepancies. - Retained `moduleNameMapper` for `.js` imports for reliable module resolution. - Ensured `cli-testing-library` imports remain in the global setup (`test/jest-setup.ts`). - Removed test cache clearing from the `test:unit` script in `package.json`. This configuration now passes reliably across environments. --- .gitignore | 3 ++- jest.config.ts | 21 +++++++++++++++++---- test/jest-setup.ts | 6 +++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index e20df5e..1e5999b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ uncaughtExceptions.log src/*.json .idea test.ts -notes.md \ No newline at end of file +notes.md +.nvmrc \ No newline at end of file diff --git a/jest.config.ts b/jest.config.ts index ee0d397..e100760 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -9,19 +9,32 @@ const config: Config = { testTimeout: 100_000, coverageProvider: 'v8', moduleDirectories: ['node_modules', 'src'], - preset: 'ts-jest/presets/js-with-ts-esm', + preset: 'ts-jest/presets/default-esm', setupFilesAfterEnv: ['/test/jest-setup.ts'], testEnvironment: 'node', testRegex: ['.*\\.test\\.ts$'], - transformIgnorePatterns: ['node_modules/(?!cli-testing-library)'], + transformIgnorePatterns: [ + 'node_modules/(?!(cli-testing-library|@clack|cleye)/.*)' + ], transform: { - '^.+\\.(ts|tsx)$': [ + '^.+\\.(ts|tsx|js|jsx|mjs)$': [ 'ts-jest', { diagnostics: false, - useESM: true + useESM: true, + tsconfig: { + module: 'ESNext', + target: 'ES2022' + } } ] + }, + // Fix Haste module naming collision + modulePathIgnorePatterns: [ + '/test/e2e/prompt-module/data/' + ], + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1' } }; diff --git a/test/jest-setup.ts b/test/jest-setup.ts index 29280f7..cd7b699 100644 --- a/test/jest-setup.ts +++ b/test/jest-setup.ts @@ -1,10 +1,10 @@ -import 'cli-testing-library/extend-expect' -import { configure } from 'cli-testing-library' import { jest } from '@jest/globals'; +import 'cli-testing-library/extend-expect'; +import { configure } from 'cli-testing-library'; global.jest = jest; /** * Adjusted the wait time for waitFor/findByText to 2000ms, because the default 1000ms makes the test results flaky */ -configure({ asyncUtilTimeout: 2000 }) +configure({ asyncUtilTimeout: 2000 }); From 83b6e0bbaf003b8b1983c0e4c9bd72775a86eecc Mon Sep 17 00:00:00 2001 From: Jethro Yu Date: Fri, 25 Apr 2025 18:39:04 +0800 Subject: [PATCH 07/10] fix(removeContentTags): keep newlines to preserve formatting The space normalization logic is updated to replace only multiple spaces and tabs with a single space, while preserving newlines. This change ensures that the formatting of the content is maintained, especially when dealing with empty line requirements and max line length. --- src/utils/removeContentTags.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/removeContentTags.ts b/src/utils/removeContentTags.ts index d478434..c665bf7 100644 --- a/src/utils/removeContentTags.ts +++ b/src/utils/removeContentTags.ts @@ -43,9 +43,9 @@ export function removeContentTags(content: result += content[i]; } } - - // Normalize spaces (replace multiple spaces with a single space) - result = result.replace(/\s+/g, ' ').trim(); - + + // Normalize multiple spaces/tabs into a single space (preserves newlines), then trim. + result = result.replace(/[ \t]+/g, ' ').trim(); + return result as unknown as T; } From 6c48c935e252ed7091f57c5fe5ae4ca9b2d3d51d Mon Sep 17 00:00:00 2001 From: EmilienMottet Date: Tue, 29 Apr 2025 20:51:24 +0200 Subject: [PATCH 08/10] =?UTF-8?q?=E2=9C=A8=20add=20custom=20HTTP=20headers?= =?UTF-8?q?=20support=20via=20OCO=5FAPI=5FCUSTOM=5FHEADERS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add OCO_API_CUSTOM_HEADERS variable to README, config enum, and env parsing to allow JSON string of custom headers. Validate that custom headers are valid JSON in config validator. Extend AiEngineConfig with customHeaders and pass headers to OllamaEngine and OpenAiEngine clients when creating requests. Parse custom headers in utils/engine and warn on invalid format. Add unit tests to ensure OCO_API_CUSTOM_HEADERS is handled correctly and merged from env over global config. This enables users to send additional headers such as Authorization or tracing headers with LLM API calls. --- README.md | 1 + src/commands/config.ts | 19 +++++++++++++++++++ src/engine/Engine.ts | 1 + src/engine/ollama.ts | 9 ++++++++- src/engine/openAi.ts | 33 ++++++++++++++++++++++++++++----- src/utils/engine.ts | 19 ++++++++++++++++++- test/unit/config.test.ts | 24 ++++++++++++++++++++++++ 7 files changed, 99 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 74c4229..b66a84b 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ Create a `.env` file and add OpenCommit config variables there like this: OCO_AI_PROVIDER= OCO_API_KEY= // or other LLM provider API token OCO_API_URL= +OCO_API_CUSTOM_HEADERS= OCO_TOKENS_MAX_INPUT= OCO_TOKENS_MAX_OUTPUT= OCO_DESCRIPTION= diff --git a/src/commands/config.ts b/src/commands/config.ts index 7e30cd5..e2dcb4f 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -25,6 +25,7 @@ export enum CONFIG_KEYS { OCO_ONE_LINE_COMMIT = 'OCO_ONE_LINE_COMMIT', OCO_TEST_MOCK_TYPE = 'OCO_TEST_MOCK_TYPE', OCO_API_URL = 'OCO_API_URL', + OCO_API_CUSTOM_HEADERS = 'OCO_API_CUSTOM_HEADERS', OCO_OMIT_SCOPE = 'OCO_OMIT_SCOPE', OCO_GITPUSH = 'OCO_GITPUSH' // todo: deprecate } @@ -204,6 +205,22 @@ export const configValidators = { return value; }, + [CONFIG_KEYS.OCO_API_CUSTOM_HEADERS](value) { + try { + // Custom headers must be a valid JSON string + if (typeof value === 'string') { + JSON.parse(value); + } + return value; + } catch (error) { + validateConfig( + CONFIG_KEYS.OCO_API_CUSTOM_HEADERS, + false, + 'Must be a valid JSON string of headers' + ); + } + }, + [CONFIG_KEYS.OCO_TOKENS_MAX_INPUT](value: any) { value = parseInt(value); validateConfig( @@ -380,6 +397,7 @@ export type ConfigType = { [CONFIG_KEYS.OCO_TOKENS_MAX_INPUT]: number; [CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT]: number; [CONFIG_KEYS.OCO_API_URL]?: string; + [CONFIG_KEYS.OCO_API_CUSTOM_HEADERS]?: string; [CONFIG_KEYS.OCO_DESCRIPTION]: boolean; [CONFIG_KEYS.OCO_EMOJI]: boolean; [CONFIG_KEYS.OCO_WHY]: boolean; @@ -462,6 +480,7 @@ const getEnvConfig = (envPath: string) => { OCO_MODEL: process.env.OCO_MODEL, OCO_API_URL: process.env.OCO_API_URL, OCO_API_KEY: process.env.OCO_API_KEY, + OCO_API_CUSTOM_HEADERS: process.env.OCO_API_CUSTOM_HEADERS, OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER as OCO_AI_PROVIDER_ENUM, OCO_TOKENS_MAX_INPUT: parseConfigVarValue(process.env.OCO_TOKENS_MAX_INPUT), diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index 1956227..c5bd2e4 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -11,6 +11,7 @@ export interface AiEngineConfig { maxTokensOutput: number; maxTokensInput: number; baseURL?: string; + customHeaders?: Record; } type Client = diff --git a/src/engine/ollama.ts b/src/engine/ollama.ts index 2d21d63..7d0355b 100644 --- a/src/engine/ollama.ts +++ b/src/engine/ollama.ts @@ -11,11 +11,18 @@ export class OllamaEngine implements AiEngine { constructor(config) { this.config = config; + + // Combine base headers with custom headers + const headers = { + 'Content-Type': 'application/json', + ...config.customHeaders + }; + this.client = axios.create({ url: config.baseURL ? `${config.baseURL}/${config.apiKey}` : 'http://localhost:11434/api/chat', - headers: { 'Content-Type': 'application/json' } + headers }); } diff --git a/src/engine/openAi.ts b/src/engine/openAi.ts index 4e1c6a9..9f2a1c8 100644 --- a/src/engine/openAi.ts +++ b/src/engine/openAi.ts @@ -14,11 +14,34 @@ export class OpenAiEngine implements AiEngine { constructor(config: OpenAiConfig) { this.config = config; - if (!config.baseURL) { - this.client = new OpenAI({ apiKey: config.apiKey }); - } else { - this.client = new OpenAI({ apiKey: config.apiKey, baseURL: config.baseURL }); + // Configuration options for the OpenAI client + const clientOptions: any = { + apiKey: config.apiKey + }; + + // Add baseURL if present + if (config.baseURL) { + clientOptions.baseURL = config.baseURL; } + + // Add custom headers if present + if (config.customHeaders) { + try { + let headers = config.customHeaders; + // If the headers are a string, try to parse them as JSON + if (typeof config.customHeaders === 'string') { + headers = JSON.parse(config.customHeaders); + } + + if (headers && typeof headers === 'object' && Object.keys(headers).length > 0) { + clientOptions.defaultHeaders = headers; + } + } catch (error) { + // Silently ignore parsing errors + } + } + + this.client = new OpenAI(clientOptions); } public generateCommitMessage = async ( @@ -42,7 +65,7 @@ export class OpenAiEngine implements AiEngine { this.config.maxTokensInput - this.config.maxTokensOutput ) throw new Error(GenerateCommitMessageErrorEnum.tooMuchTokens); - + const completion = await this.client.chat.completions.create(params); const message = completion.choices[0].message; diff --git a/src/utils/engine.ts b/src/utils/engine.ts index 3137a05..e4a9cea 100644 --- a/src/utils/engine.ts +++ b/src/utils/engine.ts @@ -16,12 +16,29 @@ export function getEngine(): AiEngine { const config = getConfig(); const provider = config.OCO_AI_PROVIDER; + // Parse custom headers if provided + let customHeaders = {}; + if (config.OCO_API_CUSTOM_HEADERS) { + try { + // If it's already an object, no need to parse it + if (typeof config.OCO_API_CUSTOM_HEADERS === 'object' && !Array.isArray(config.OCO_API_CUSTOM_HEADERS)) { + customHeaders = config.OCO_API_CUSTOM_HEADERS; + } else { + // Try to parse as JSON + customHeaders = JSON.parse(config.OCO_API_CUSTOM_HEADERS); + } + } catch (error) { + console.warn('Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers'); + } + } + const DEFAULT_CONFIG = { model: config.OCO_MODEL!, maxTokensOutput: config.OCO_TOKENS_MAX_OUTPUT!, maxTokensInput: config.OCO_TOKENS_MAX_INPUT!, baseURL: config.OCO_API_URL!, - apiKey: config.OCO_API_KEY! + apiKey: config.OCO_API_KEY!, + customHeaders // Add custom headers to the configuration }; switch (provider) { diff --git a/test/unit/config.test.ts b/test/unit/config.test.ts index 89ffc7e..871655d 100644 --- a/test/unit/config.test.ts +++ b/test/unit/config.test.ts @@ -122,6 +122,30 @@ describe('config', () => { expect(config.OCO_ONE_LINE_COMMIT).toEqual(false); expect(config.OCO_OMIT_SCOPE).toEqual(true); }); + + it('should handle custom HTTP headers correctly', async () => { + globalConfigFile = await generateConfig('.opencommit', { + OCO_API_CUSTOM_HEADERS: '{"X-Global-Header": "global-value"}' + }); + + envConfigFile = await generateConfig('.env', { + OCO_API_CUSTOM_HEADERS: '{"Authorization": "Bearer token123", "X-Custom-Header": "test-value"}' + }); + + const config = getConfig({ + globalPath: globalConfigFile.filePath, + envPath: envConfigFile.filePath + }); + + expect(config).not.toEqual(null); + expect(config.OCO_API_CUSTOM_HEADERS).toEqual('{"Authorization": "Bearer token123", "X-Custom-Header": "test-value"}'); + + // Verify that the JSON can be parsed correctly + const parsedHeaders = JSON.parse(config.OCO_API_CUSTOM_HEADERS); + expect(parsedHeaders).toHaveProperty('Authorization', 'Bearer token123'); + expect(parsedHeaders).toHaveProperty('X-Custom-Header', 'test-value'); + expect(parsedHeaders).not.toHaveProperty('X-Global-Header'); + }); it('should handle empty local config correctly', async () => { globalConfigFile = await generateConfig('.opencommit', { From 71a44fac284d4438dc90c274d7d04cb5b635de1d Mon Sep 17 00:00:00 2001 From: EmilienMottet Date: Wed, 30 Apr 2025 14:46:54 +0200 Subject: [PATCH 09/10] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20OpenAI=20?= =?UTF-8?q?client=20options=20and=20unify=20custom=20headers=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use OpenAI.ClientOptions for stronger typing and clarity Extract custom headers parsing into parseCustomHeaders util Simplify getEngine by delegating header parsing to helper Improve maintainability and reduce code duplication --- src/engine/openAi.ts | 7 +------ src/utils/engine.ts | 38 ++++++++++++++++++++++---------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/engine/openAi.ts b/src/engine/openAi.ts index 9f2a1c8..f87bcd1 100644 --- a/src/engine/openAi.ts +++ b/src/engine/openAi.ts @@ -14,21 +14,17 @@ export class OpenAiEngine implements AiEngine { constructor(config: OpenAiConfig) { this.config = config; - // Configuration options for the OpenAI client - const clientOptions: any = { + const clientOptions: OpenAI.ClientOptions = { apiKey: config.apiKey }; - // Add baseURL if present if (config.baseURL) { clientOptions.baseURL = config.baseURL; } - // Add custom headers if present if (config.customHeaders) { try { let headers = config.customHeaders; - // If the headers are a string, try to parse them as JSON if (typeof config.customHeaders === 'string') { headers = JSON.parse(config.customHeaders); } @@ -37,7 +33,6 @@ export class OpenAiEngine implements AiEngine { clientOptions.defaultHeaders = headers; } } catch (error) { - // Silently ignore parsing errors } } diff --git a/src/utils/engine.ts b/src/utils/engine.ts index e4a9cea..e265b5b 100644 --- a/src/utils/engine.ts +++ b/src/utils/engine.ts @@ -12,25 +12,31 @@ import { GroqEngine } from '../engine/groq'; import { MLXEngine } from '../engine/mlx'; import { DeepseekEngine } from '../engine/deepseek'; +function parseCustomHeaders(headers: any): Record { + let parsedHeaders = {}; + + if (!headers) { + return parsedHeaders; + } + + try { + if (typeof headers === 'object' && !Array.isArray(headers)) { + parsedHeaders = headers; + } else { + parsedHeaders = JSON.parse(headers); + } + } catch (error) { + console.warn('Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers'); + } + + return parsedHeaders; +} + export function getEngine(): AiEngine { const config = getConfig(); const provider = config.OCO_AI_PROVIDER; - // Parse custom headers if provided - let customHeaders = {}; - if (config.OCO_API_CUSTOM_HEADERS) { - try { - // If it's already an object, no need to parse it - if (typeof config.OCO_API_CUSTOM_HEADERS === 'object' && !Array.isArray(config.OCO_API_CUSTOM_HEADERS)) { - customHeaders = config.OCO_API_CUSTOM_HEADERS; - } else { - // Try to parse as JSON - customHeaders = JSON.parse(config.OCO_API_CUSTOM_HEADERS); - } - } catch (error) { - console.warn('Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers'); - } - } + const customHeaders = parseCustomHeaders(config.OCO_API_CUSTOM_HEADERS); const DEFAULT_CONFIG = { model: config.OCO_MODEL!, @@ -38,7 +44,7 @@ export function getEngine(): AiEngine { maxTokensInput: config.OCO_TOKENS_MAX_INPUT!, baseURL: config.OCO_API_URL!, apiKey: config.OCO_API_KEY!, - customHeaders // Add custom headers to the configuration + customHeaders }; switch (provider) { From 6aae1c7bd78f768e7ad0f618c8edea54446017d5 Mon Sep 17 00:00:00 2001 From: EmilienMottet Date: Wed, 30 Apr 2025 21:43:44 +0200 Subject: [PATCH 10/10] =?UTF-8?q?=E2=99=BB=EF=B8=8F(engine):=20extract=20c?= =?UTF-8?q?ustom=20header=20parsing=20and=20update=20OpenAiEngine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - export parseCustomHeaders from src/utils/engine.ts - use parseCustomHeaders in OpenAiEngine for config.customHeaders - remove try/catch and inline JSON.parse logic - update config test to expect headers as object and drop JSON.parse Centralize header parsing for reuse and simplify engine code Update tests to match new header format for clarity --- src/engine/openAi.ts | 14 ++++---------- src/utils/engine.ts | 2 +- test/unit/config.test.ts | 6 +++--- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/engine/openAi.ts b/src/engine/openAi.ts index f87bcd1..22a9b37 100644 --- a/src/engine/openAi.ts +++ b/src/engine/openAi.ts @@ -1,6 +1,7 @@ import axios from 'axios'; import { OpenAI } from 'openai'; import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff'; +import { parseCustomHeaders } from '../utils/engine'; import { removeContentTags } from '../utils/removeContentTags'; import { tokenCount } from '../utils/tokenCount'; import { AiEngine, AiEngineConfig } from './Engine'; @@ -23,16 +24,9 @@ export class OpenAiEngine implements AiEngine { } if (config.customHeaders) { - try { - let headers = config.customHeaders; - if (typeof config.customHeaders === 'string') { - headers = JSON.parse(config.customHeaders); - } - - if (headers && typeof headers === 'object' && Object.keys(headers).length > 0) { - clientOptions.defaultHeaders = headers; - } - } catch (error) { + const headers = parseCustomHeaders(config.customHeaders); + if (Object.keys(headers).length > 0) { + clientOptions.defaultHeaders = headers; } } diff --git a/src/utils/engine.ts b/src/utils/engine.ts index e265b5b..dbc45a0 100644 --- a/src/utils/engine.ts +++ b/src/utils/engine.ts @@ -12,7 +12,7 @@ import { GroqEngine } from '../engine/groq'; import { MLXEngine } from '../engine/mlx'; import { DeepseekEngine } from '../engine/deepseek'; -function parseCustomHeaders(headers: any): Record { +export function parseCustomHeaders(headers: any): Record { let parsedHeaders = {}; if (!headers) { diff --git a/test/unit/config.test.ts b/test/unit/config.test.ts index 871655d..fc4709d 100644 --- a/test/unit/config.test.ts +++ b/test/unit/config.test.ts @@ -138,10 +138,10 @@ describe('config', () => { }); expect(config).not.toEqual(null); - expect(config.OCO_API_CUSTOM_HEADERS).toEqual('{"Authorization": "Bearer token123", "X-Custom-Header": "test-value"}'); + expect(config.OCO_API_CUSTOM_HEADERS).toEqual({"Authorization": "Bearer token123", "X-Custom-Header": "test-value"}); - // Verify that the JSON can be parsed correctly - const parsedHeaders = JSON.parse(config.OCO_API_CUSTOM_HEADERS); + // No need to parse JSON again since it's already an object + const parsedHeaders = config.OCO_API_CUSTOM_HEADERS; expect(parsedHeaders).toHaveProperty('Authorization', 'Bearer token123'); expect(parsedHeaders).toHaveProperty('X-Custom-Header', 'test-value'); expect(parsedHeaders).not.toHaveProperty('X-Global-Header');