Skip to content

Commit ba0d711

Browse files
committed
feat: add Codex AI coding tool support
Add support for detecting Codex AI coding tool through environment variables CODEX_MANAGED_BY_NPM and CODEX_MANAGED_BY_BUN. Changes: - Create src/ai-tools/codex.ts configuration file - Add Codex to AI tools index with CLI type (highest priority) - Update test expectations from 8 to 9 tools - Add comprehensive tests for Codex environment variables: * Integration tests for CODEX_MANAGED_BY_NPM and CODEX_MANAGED_BY_BUN * Unit tests in exec.test.ts for getCoDevelopedBy function * Production build tests for compiled code * Installed package tests for npm pack scenarios The Codex tool is detected when either CODEX_MANAGED_BY_NPM=1 or CODEX_MANAGED_BY_BUN=1 is set, and will add "Co-developed-by: Codex <noreply@openai.com>" to commit messages. Change-Id: Id9ccec1427a942df35a7c0c4ecd3040c28167afa Co-developed-by: Cursor <noreply@cursor.com>
1 parent 4c7ae9d commit ba0d711

6 files changed

Lines changed: 205 additions & 3 deletions

File tree

src/ai-tools/codex.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { AIToolConfig } from './index.js';
2+
3+
const config: AIToolConfig = {
4+
type: 'cli',
5+
userName: 'Codex',
6+
userEmail: 'noreply@openai.com',
7+
envVars: [
8+
{ key: 'CODEX_MANAGED_BY_NPM', value: '1' },
9+
{ key: 'CODEX_MANAGED_BY_BUN', value: '1' },
10+
],
11+
};
12+
13+
export default config;

src/ai-tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ function compareByPriority(a: AIToolConfig, b: AIToolConfig): number {
6969

7070
// Import all tool configurations
7171
import claudeConfig from './claude.js';
72+
import codexConfig from './codex.js';
7273
import cursorConfig from './cursor.js';
7374
import geminiConfig from './gemini.js';
7475
import iflowConfig from './iflow.js';
@@ -83,6 +84,7 @@ import qwenCodeConfig from './qwen-code.js';
8384
*/
8485
const allConfigs: AIToolConfig[] = [
8586
claudeConfig,
87+
codexConfig,
8688
iflowConfig,
8789
qwenCodeConfig,
8890
geminiConfig,

test/commands/exec.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,20 @@ describe('exec command utilities', () => {
787787
expect(getCoDevelopedBy()).toBe('iFlow <noreply@iflow.cn>');
788788
});
789789

790+
it('should return Codex CoDevelopedBy when CODEX_MANAGED_BY_NPM=1 is set', () => {
791+
// Clear all environment variables to ensure proper order testing
792+
clearCoDevelopedByEnvVars();
793+
process.env.CODEX_MANAGED_BY_NPM = '1';
794+
expect(getCoDevelopedBy()).toBe('Codex <noreply@openai.com>');
795+
});
796+
797+
it('should return Codex CoDevelopedBy when CODEX_MANAGED_BY_BUN=1 is set', () => {
798+
// Clear all environment variables to ensure proper order testing
799+
clearCoDevelopedByEnvVars();
800+
process.env.CODEX_MANAGED_BY_BUN = '1';
801+
expect(getCoDevelopedBy()).toBe('Codex <noreply@openai.com>');
802+
});
803+
790804
it('should return Kiro CoDevelopedBy when __CFBundleIdentifier=dev.kiro.desktop is set', () => {
791805
// Clear all environment variables to ensure proper order testing
792806
clearCoDevelopedByEnvVars();

test/integration.test.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,108 @@ This feature uses AI technology to enhance the application.`;
310310
);
311311
});
312312

313+
it('should work with CODEX_MANAGED_BY_NPM environment variable', () => {
314+
// Create a commit message file
315+
const messageFile = path.join(tempDir, 'codex-npm-message.txt');
316+
const commitMessage = `feat: implement feature with Codex
317+
318+
This feature was developed using Codex via npm.`;
319+
writeFileSync(messageFile, commitMessage, 'utf8');
320+
321+
// Test with CODEX_MANAGED_BY_NPM environment variable, but first unset higher priority variables
322+
const env = {
323+
...process.env,
324+
CLAUDECODE: undefined,
325+
CODEX_MANAGED_BY_NPM: '1',
326+
QWEN_CODE: undefined,
327+
GEMINI_CLI: undefined,
328+
VSCODE_BRAND: undefined,
329+
CURSOR_TRACE_ID: undefined,
330+
};
331+
332+
// Execute the commit-msg hook directly
333+
const execResult = spawnSync(
334+
'node',
335+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'exec', messageFile],
336+
{
337+
cwd: testRepoDir,
338+
encoding: 'utf-8',
339+
timeout: 30000,
340+
env: env,
341+
}
342+
);
343+
344+
expect(execResult.status).toBe(0);
345+
346+
// Read the processed commit message
347+
const processedMessage = readFileSync(messageFile, 'utf8');
348+
349+
// Should have added Change-Id
350+
expect(processedMessage).toMatch(/Change-Id: I[a-f0-9]{8,}/);
351+
352+
// Should have added Co-developed-by for Codex
353+
expect(processedMessage).toContain(
354+
'Co-developed-by: Codex <noreply@openai.com>'
355+
);
356+
357+
// Verify original content is preserved
358+
expect(processedMessage).toContain('feat: implement feature with Codex');
359+
expect(processedMessage).toContain(
360+
'This feature was developed using Codex via npm.'
361+
);
362+
});
363+
364+
it('should work with CODEX_MANAGED_BY_BUN environment variable', () => {
365+
// Create a commit message file
366+
const messageFile = path.join(tempDir, 'codex-bun-message.txt');
367+
const commitMessage = `feat: implement feature with Codex
368+
369+
This feature was developed using Codex via bun.`;
370+
writeFileSync(messageFile, commitMessage, 'utf8');
371+
372+
// Test with CODEX_MANAGED_BY_BUN environment variable, but first unset higher priority variables
373+
const env = {
374+
...process.env,
375+
CLAUDECODE: undefined,
376+
CODEX_MANAGED_BY_BUN: '1',
377+
QWEN_CODE: undefined,
378+
GEMINI_CLI: undefined,
379+
VSCODE_BRAND: undefined,
380+
CURSOR_TRACE_ID: undefined,
381+
};
382+
383+
// Execute the commit-msg hook directly
384+
const execResult = spawnSync(
385+
'node',
386+
[path.join(originalCwd, 'dist/bin/commit-msg.js'), 'exec', messageFile],
387+
{
388+
cwd: testRepoDir,
389+
encoding: 'utf-8',
390+
timeout: 30000,
391+
env: env,
392+
}
393+
);
394+
395+
expect(execResult.status).toBe(0);
396+
397+
// Read the processed commit message
398+
const processedMessage = readFileSync(messageFile, 'utf8');
399+
400+
// Should have added Change-Id
401+
expect(processedMessage).toMatch(/Change-Id: I[a-f0-9]{8,}/);
402+
403+
// Should have added Co-developed-by for Codex
404+
expect(processedMessage).toContain(
405+
'Co-developed-by: Codex <noreply@openai.com>'
406+
);
407+
408+
// Verify original content is preserved
409+
expect(processedMessage).toContain('feat: implement feature with Codex');
410+
expect(processedMessage).toContain(
411+
'This feature was developed using Codex via bun.'
412+
);
413+
});
414+
313415
it('should work with disabled CoDevelopedBy configuration', () => {
314416
// Create a commit message file
315417
const messageFile = path.join(tempDir, 'no-co-developed-message.txt');

test/pack.test.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,62 @@ describe('commit-msg CLI npm pack tests', () => {
413413
);
414414
});
415415

416+
it('should generate Co-developed-by for CODEX_MANAGED_BY_NPM in installed package', () => {
417+
const messageFile = path.join(tempDir, 'codevelopedby-codex-npm.txt');
418+
const commitMessage = 'feat: test codex npm co-developed-by';
419+
writeFileSync(messageFile, commitMessage, 'utf8');
420+
421+
const env = {
422+
...process.env,
423+
CODEX_MANAGED_BY_NPM: '1',
424+
};
425+
426+
const execResult = spawnSync(
427+
'node',
428+
[installedBinPath, 'exec', messageFile],
429+
{
430+
encoding: 'utf-8',
431+
timeout: 30000,
432+
env: env,
433+
}
434+
);
435+
436+
expect(execResult.status).toBe(0);
437+
438+
const processedMessage = readFileSync(messageFile, 'utf8');
439+
expect(processedMessage).toContain(
440+
'Co-developed-by: Codex <noreply@openai.com>'
441+
);
442+
});
443+
444+
it('should generate Co-developed-by for CODEX_MANAGED_BY_BUN in installed package', () => {
445+
const messageFile = path.join(tempDir, 'codevelopedby-codex-bun.txt');
446+
const commitMessage = 'feat: test codex bun co-developed-by';
447+
writeFileSync(messageFile, commitMessage, 'utf8');
448+
449+
const env = {
450+
...process.env,
451+
CODEX_MANAGED_BY_BUN: '1',
452+
};
453+
454+
const execResult = spawnSync(
455+
'node',
456+
[installedBinPath, 'exec', messageFile],
457+
{
458+
encoding: 'utf-8',
459+
timeout: 30000,
460+
env: env,
461+
}
462+
);
463+
464+
expect(execResult.status).toBe(0);
465+
466+
const processedMessage = readFileSync(messageFile, 'utf8');
467+
expect(processedMessage).toContain(
468+
'Co-developed-by: Codex <noreply@openai.com>'
469+
);
470+
});
471+
416472
it('should respect priority order (CLI over IDE) in installed package', () => {
417473
const messageFile = path.join(tempDir, 'priority-test.txt');
418474
const commitMessage = 'feat: test priority';
@@ -462,7 +518,7 @@ describe('commit-msg CLI npm pack tests', () => {
462518
const configs = installedAITools.getAllToolConfigs();
463519
expect(configs).toBeDefined();
464520
expect(Array.isArray(configs)).toBe(true);
465-
expect(configs.length).toBe(8); // Should have 8 tools
521+
expect(configs.length).toBe(9); // Should have 9 tools
466522

467523
// Verify all configs have required fields
468524
for (const config of configs) {

test/production-build.test.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ describe('Production Build Tests', () => {
5252
describe('Configuration Structure Tests', () => {
5353
it('should have correct number of tool configurations', () => {
5454
const configs = productionAITools.getAllToolConfigs();
55-
// Should have 8 tools: claude, iflow, qwen-code, gemini, qoder-cli, cursor, kiro, qoder-ide
56-
expect(configs.length).toBe(8);
55+
// Should have 9 tools: claude, codex, iflow, qwen-code, gemini, qoder-cli, cursor, kiro, qoder-ide
56+
expect(configs.length).toBe(9);
5757
});
5858

5959
it('should have all required fields in each configuration', () => {
@@ -123,6 +123,7 @@ describe('Production Build Tests', () => {
123123
const configs = productionAITools.getAllToolConfigs();
124124
const toolNames = configs.map((c) => c.userName);
125125
const expectedTools = [
126+
'Codex',
126127
'Claude',
127128
'iFlow',
128129
'Qwen-Coder',
@@ -196,6 +197,20 @@ describe('Production Build Tests', () => {
196197
expect(result).toBe('Cursor <noreply@cursor.com>');
197198
});
198199

200+
it('should return Codex CoDevelopedBy when CODEX_MANAGED_BY_NPM=1 is set', () => {
201+
productionExec.clearCoDevelopedByEnvVars();
202+
process.env.CODEX_MANAGED_BY_NPM = '1';
203+
const result = productionExec.getCoDevelopedBy();
204+
expect(result).toBe('Codex <noreply@openai.com>');
205+
});
206+
207+
it('should return Codex CoDevelopedBy when CODEX_MANAGED_BY_BUN=1 is set', () => {
208+
productionExec.clearCoDevelopedByEnvVars();
209+
process.env.CODEX_MANAGED_BY_BUN = '1';
210+
const result = productionExec.getCoDevelopedBy();
211+
expect(result).toBe('Codex <noreply@openai.com>');
212+
});
213+
199214
it('should return Kiro CoDevelopedBy when __CFBundleIdentifier=dev.kiro.desktop is set', () => {
200215
productionExec.clearCoDevelopedByEnvVars();
201216
process.env.__CFBundleIdentifier = 'dev.kiro.desktop';

0 commit comments

Comments
 (0)