diff --git a/package-lock.json b/package-lock.json index ca6a7c4..a4461f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "commander": "^13.1.0", + "minimatch": "^10.0.3", "update-notifier": "^7.3.1" }, "bin": { @@ -740,6 +741,27 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1528,6 +1550,22 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { "version": "8.40.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz", @@ -1930,7 +1968,7 @@ }, "node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", @@ -3363,16 +3401,15 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, + "version": "10.0.3", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" diff --git a/package.json b/package.json index 34dd656..8f16261 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ }, "dependencies": { "commander": "^13.1.0", + "minimatch": "^10.0.3", "update-notifier": "^7.3.1" }, "devDependencies": { diff --git a/src/commands/exec.ts b/src/commands/exec.ts index 823fc3a..fe0eee8 100644 --- a/src/commands/exec.ts +++ b/src/commands/exec.ts @@ -5,9 +5,11 @@ import * as fs from 'fs'; import * as path from 'path'; import { spawnSync } from 'child_process'; +import { minimatch } from 'minimatch'; // Define environment variable configurations and their corresponding CoDevelopedBy values // Format: ["key=value", "co-developed-by-string"] +// Use glob patterns for value matching with ** to match any characters including / const envConfigs: [string, string][] = [ // We can run CLI in IDE (such as Cursor and Qoder), so check CLI env variables first ['CLAUDECODE=1', 'Claude '], @@ -19,6 +21,14 @@ const envConfigs: [string, string][] = [ ['__CFBundleIdentifier=dev.kiro.desktop', 'Kiro '], ['VSCODE_BRAND=Qoder', 'Qoder '], ['__CFBundleIdentifier=com.qoder.ide', 'Qoder '], // Use this unstable variable until Qoder has a better one + // Check env variables for IDEs in remove development environments + [ + 'VSCODE_GIT_ASKPASS_MAIN=**/.cursor-server/**', + 'Cursor ', + ], + ['BROWSER=**/.cursor-server/**', 'Cursor '], + ['VSCODE_GIT_ASKPASS_MAIN=**/.qoder-server/**', 'Qoder '], + ['BROWSER=**/.qoder-server/**', 'Qoder '], ]; /** @@ -278,23 +288,14 @@ function getCoDevelopedBy(): string { continue; } - // First check for exact match - if ( - expectedValue !== null && - expectedValue !== '*' && - actualValue === expectedValue - ) { - return coDevelopedBy; - } - - // For wildcard cases (*) or null for expectedValue, only return CoDevelopedBy - // if the value is actually meaningful - if (expectedValue === '*' || expectedValue === null) { + // For null expectedValue (just check key existence) + if (expectedValue === null) { // Only return CoDevelopedBy if the actual value is truthy (not empty, not '0', not 'false', etc.) if ( actualValue && actualValue !== '0' && actualValue !== 'false' && + actualValue !== 'off' && actualValue !== 'no' ) { return coDevelopedBy; @@ -302,6 +303,11 @@ function getCoDevelopedBy(): string { // Continue to next configuration if value is falsy continue; } + + // Use minimatch for glob pattern matching + if (minimatch(actualValue, expectedValue, { dot: true })) { + return coDevelopedBy; + } } // Return empty string if none of the environment configurations match diff --git a/test/commands/exec.test.ts b/test/commands/exec.test.ts index a51071a..f95db78 100644 --- a/test/commands/exec.test.ts +++ b/test/commands/exec.test.ts @@ -737,6 +737,33 @@ describe('exec command utilities', () => { process.env.CLAUDECODE = ''; expect(getCoDevelopedBy()).toBe(''); }); + + // Enhanced tests for Cursor and Qoder detection + it('should return Cursor CoDevelopedBy when VSCODE_GIT_ASKPASS_MAIN contains .cursor-server', () => { + clearCoDevelopedByEnvVars(); + process.env.VSCODE_GIT_ASKPASS_MAIN = + '/home/user/.cursor-server/bin/askpass-main.js'; + expect(getCoDevelopedBy()).toBe('Cursor '); + }); + + it('should return Cursor CoDevelopedBy when BROWSER contains .cursor-server', () => { + clearCoDevelopedByEnvVars(); + process.env.BROWSER = '/home/user/.cursor-server/bin/helpers/browser.sh'; + expect(getCoDevelopedBy()).toBe('Cursor '); + }); + + it('should return Qoder CoDevelopedBy when VSCODE_GIT_ASKPASS_MAIN contains .qoder-server', () => { + clearCoDevelopedByEnvVars(); + process.env.VSCODE_GIT_ASKPASS_MAIN = + '/home/user/.qoder-server/bin/askpass-main.js'; + expect(getCoDevelopedBy()).toBe('Qoder '); + }); + + it('should return Qoder CoDevelopedBy when BROWSER contains .qoder-server', () => { + clearCoDevelopedByEnvVars(); + process.env.BROWSER = '/home/user/.qoder-server/bin/helpers/browser.sh'; + expect(getCoDevelopedBy()).toBe('Qoder '); + }); }); describe('hasCoDevelopedBy', () => {