claude-yes 1.31.2 → 1.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +225 -21
  2. package/dist/agent-yes.js +2 -0
  3. package/dist/amp-yes.js +2 -0
  4. package/dist/auggie-yes.js +2 -0
  5. package/dist/claude-yes.js +2 -20432
  6. package/dist/cli.js +18341 -10955
  7. package/dist/cli.js.map +141 -150
  8. package/dist/codex-yes.js +2 -20432
  9. package/dist/copilot-yes.js +2 -20432
  10. package/dist/cursor-yes.js +2 -20432
  11. package/dist/gemini-yes.js +2 -20432
  12. package/dist/grok-yes.js +2 -20432
  13. package/dist/index.js +16258 -13586
  14. package/dist/index.js.map +176 -191
  15. package/dist/qwen-yes.js +2 -20432
  16. package/package.json +95 -84
  17. package/ts/ReadyManager.spec.ts +10 -10
  18. package/ts/ReadyManager.ts +1 -1
  19. package/ts/SUPPORTED_CLIS.ts +4 -0
  20. package/ts/catcher.spec.ts +69 -70
  21. package/ts/cli-idle.spec.ts +8 -8
  22. package/ts/cli.ts +18 -26
  23. package/ts/defineConfig.ts +4 -4
  24. package/ts/idleWaiter.spec.ts +9 -9
  25. package/ts/index.ts +474 -233
  26. package/ts/logger.ts +22 -0
  27. package/ts/parseCliArgs.spec.ts +146 -147
  28. package/ts/parseCliArgs.ts +127 -59
  29. package/ts/postbuild.ts +29 -15
  30. package/ts/pty-fix.ts +155 -0
  31. package/ts/pty.ts +19 -0
  32. package/ts/removeControlCharacters.spec.ts +37 -38
  33. package/ts/removeControlCharacters.ts +2 -1
  34. package/ts/runningLock.spec.ts +119 -125
  35. package/ts/runningLock.ts +44 -55
  36. package/ts/session-integration.spec.ts +34 -42
  37. package/ts/utils.spec.ts +35 -35
  38. package/ts/utils.ts +7 -7
  39. package/ts/codex-resume.spec.ts +0 -239
  40. package/ts/codexSessionManager.spec.ts +0 -51
  41. package/ts/codexSessionManager.test.ts +0 -259
  42. package/ts/codexSessionManager.ts +0 -312
  43. package/ts/yesLog.spec.ts +0 -74
  44. package/ts/yesLog.ts +0 -27
@@ -1,239 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
2
- import { spawn } from 'child_process';
3
- import { mkdir, rm } from 'fs/promises';
4
- import path from 'path';
5
- import { promisify } from 'util';
6
-
7
- const sleep = promisify(setTimeout);
8
-
9
- // Helper function to run codex-yes with proper error handling
10
- function runCodexYes(
11
- args: string[],
12
- cwd: string,
13
- timeout: number = 30000,
14
- ): Promise<{
15
- stdout: string;
16
- stderr: string;
17
- exitCode: number | null;
18
- }> {
19
- return new Promise((resolve) => {
20
- const child = spawn('node', ['dist/cli.js', 'codex', ...args], {
21
- cwd,
22
- stdio: 'pipe',
23
- env: { ...process.env, VERBOSE: '1' },
24
- });
25
-
26
- let stdout = '';
27
- let stderr = '';
28
-
29
- child.stdout?.on('data', (data) => {
30
- stdout += data.toString();
31
- });
32
-
33
- child.stderr?.on('data', (data) => {
34
- stderr += data.toString();
35
- });
36
-
37
- const timeoutId = setTimeout(() => {
38
- child.kill('SIGTERM');
39
- setTimeout(() => child.kill('SIGKILL'), 5000);
40
- }, timeout);
41
-
42
- child.on('exit', (code) => {
43
- clearTimeout(timeoutId);
44
- resolve({
45
- stdout,
46
- stderr,
47
- exitCode: code,
48
- });
49
- });
50
-
51
- child.on('error', (error) => {
52
- clearTimeout(timeoutId);
53
- resolve({
54
- stdout,
55
- stderr: stderr + error.message,
56
- exitCode: null,
57
- });
58
- });
59
- });
60
- }
61
-
62
- describe('Codex Session Restoration', () => {
63
- const testDir = path.join(__dirname, '../logs');
64
- const cwd1 = path.join(testDir, 'cwd1');
65
- const cwd2 = path.join(testDir, 'cwd2');
66
-
67
- beforeAll(async () => {
68
- // Create test directories
69
- await mkdir(cwd1, { recursive: true });
70
- await mkdir(cwd2, { recursive: true });
71
- });
72
-
73
- afterAll(async () => {
74
- // Clean up test directories
75
- await rm(testDir, { recursive: true, force: true });
76
- });
77
-
78
- it('should maintain separate sessions for different directories', async () => {
79
- console.log('\n=== Testing Codex Session Restoration ===\n');
80
-
81
- // Step 1: Start codex-yes in cwd1 with 60s timeout (background)
82
- console.log('Step 1: Starting codex-yes in cwd1 (60s timeout)...');
83
- const cwd1Promise = runCodexYes(
84
- ['-e', '60s', 'hello from cwd1'],
85
- cwd1,
86
- 70000,
87
- );
88
-
89
- // Wait a bit for cwd1 to initialize
90
- await sleep(2000);
91
-
92
- // Step 2: Start codex-yes in cwd2 with 5s timeout (foreground)
93
- console.log('Step 2: Starting codex-yes in cwd2 (5s timeout)...');
94
- const cwd2Result = await runCodexYes(
95
- ['-e', '5s', 'hello from cwd2'],
96
- cwd2,
97
- 15000,
98
- );
99
-
100
- console.log('Step 2 completed - cwd2 result:');
101
- console.log('- Exit code:', cwd2Result.exitCode);
102
- console.log('- Stdout length:', cwd2Result.stdout.length);
103
- console.log('- Stderr length:', cwd2Result.stderr.length);
104
-
105
- // Step 3: Wait for cwd1 to complete
106
- console.log('Step 3: Waiting for cwd1 to complete...');
107
- const cwd1Result = await cwd1Promise;
108
-
109
- console.log('Step 3 completed - cwd1 result:');
110
- console.log('- Exit code:', cwd1Result.exitCode);
111
- console.log('- Stdout length:', cwd1Result.stdout.length);
112
- console.log('- Stderr length:', cwd1Result.stderr.length);
113
-
114
- // Step 4: Test session restoration in cwd1 using --continue
115
- console.log(
116
- 'Step 4: Testing session restoration in cwd1 with --continue...',
117
- );
118
- const continueResult = await runCodexYes(
119
- ['--continue', '-e', '10s', 'hello3 continuing session'],
120
- cwd1,
121
- 20000,
122
- );
123
-
124
- console.log('Step 4 completed - continue result:');
125
- console.log('- Exit code:', continueResult.exitCode);
126
- console.log('- Stdout length:', continueResult.stdout.length);
127
- console.log('- Stderr length:', continueResult.stderr.length);
128
-
129
- // Analyze results
130
- console.log('\n=== Analysis ===');
131
-
132
- // Check that sessions ran (allowing for various exit codes since codex might not be available)
133
- const cwd1Ran =
134
- cwd1Result.stdout.length > 0 || cwd1Result.stderr.length > 0;
135
- const cwd2Ran =
136
- cwd2Result.stdout.length > 0 || cwd2Result.stderr.length > 0;
137
- const continueRan =
138
- continueResult.stdout.length > 0 || continueResult.stderr.length > 0;
139
-
140
- console.log('Sessions executed:');
141
- console.log('- cwd1:', cwd1Ran ? 'YES' : 'NO');
142
- console.log('- cwd2:', cwd2Ran ? 'YES' : 'NO');
143
- console.log('- continue:', continueRan ? 'YES' : 'NO');
144
-
145
- // Look for session-related logs in the outputs
146
- const hasSessionLogs = (output: string) => {
147
- return (
148
- output.includes('session|') ||
149
- output.includes('continue|') ||
150
- output.includes('restore|') ||
151
- output.includes('Session ID') ||
152
- output.includes('resume')
153
- );
154
- };
155
-
156
- const cwd1HasSessionLogs = hasSessionLogs(
157
- cwd1Result.stdout + cwd1Result.stderr,
158
- );
159
- const cwd2HasSessionLogs = hasSessionLogs(
160
- cwd2Result.stdout + cwd2Result.stderr,
161
- );
162
- const continueHasSessionLogs = hasSessionLogs(
163
- continueResult.stdout + continueResult.stderr,
164
- );
165
-
166
- console.log('Session management logs found:');
167
- console.log('- cwd1:', cwd1HasSessionLogs ? 'YES' : 'NO');
168
- console.log('- cwd2:', cwd2HasSessionLogs ? 'YES' : 'NO');
169
- console.log('- continue:', continueHasSessionLogs ? 'YES' : 'NO');
170
-
171
- // Extract any visible session IDs or relevant logs
172
- const extractRelevantLogs = (output: string, label: string) => {
173
- const lines = output.split('\n');
174
- const relevantLines = lines.filter(
175
- (line) =>
176
- line.includes('session|') ||
177
- line.includes('continue|') ||
178
- line.includes('restore|') ||
179
- line.includes('Session') ||
180
- line.includes('resume') ||
181
- line.includes('UUID') ||
182
- /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i.test(
183
- line,
184
- ),
185
- );
186
-
187
- if (relevantLines.length > 0) {
188
- console.log(`\n${label} relevant logs:`);
189
- relevantLines.forEach((line) => console.log(` ${line.trim()}`));
190
- }
191
- };
192
-
193
- extractRelevantLogs(cwd1Result.stdout + cwd1Result.stderr, 'CWD1');
194
- extractRelevantLogs(cwd2Result.stdout + cwd2Result.stderr, 'CWD2');
195
- extractRelevantLogs(
196
- continueResult.stdout + continueResult.stderr,
197
- 'CONTINUE',
198
- );
199
-
200
- // Basic assertions - the test should at least attempt to run
201
- expect(cwd1Ran || cwd2Ran || continueRan).toBe(true);
202
-
203
- // If codex is available and working, we should see some session management
204
- if (cwd1Result.exitCode === 0 && cwd2Result.exitCode === 0) {
205
- console.log(
206
- '\nCodex appears to be working - checking for session management...',
207
- );
208
- // At least one of the runs should show session management activity
209
- expect(
210
- cwd1HasSessionLogs || cwd2HasSessionLogs || continueHasSessionLogs,
211
- ).toBe(true);
212
- } else {
213
- console.log(
214
- '\nCodex may not be available or working - test completed with basic execution check',
215
- );
216
- }
217
-
218
- console.log('\n=== Test Summary ===');
219
- console.log('✅ Session restoration test completed');
220
- console.log('✅ Multiple directories tested');
221
- console.log('✅ Continue functionality tested');
222
- console.log('✅ Session isolation verified');
223
- }, 120000); // 2 minute timeout for the entire test
224
-
225
- it('should handle missing codex gracefully', async () => {
226
- console.log('\n=== Testing Error Handling ===\n');
227
-
228
- // Test with a simple command to ensure our wrapper handles missing codex
229
- const result = await runCodexYes(['--help'], cwd1, 5000);
230
-
231
- // Should either work (if codex is installed) or fail gracefully
232
- const hasOutput = result.stdout.length > 0 || result.stderr.length > 0;
233
- expect(hasOutput).toBe(true);
234
-
235
- console.log('Error handling test completed');
236
- console.log('- Has output:', hasOutput);
237
- console.log('- Exit code:', result.exitCode);
238
- });
239
- });
@@ -1,51 +0,0 @@
1
- import { describe, expect, it } from 'bun:test';
2
- import {
3
- extractSessionId,
4
- extractSessionIdFromSessionMeta,
5
- } from './codexSessionManager';
6
-
7
- describe('codexSessionManager', () => {
8
- describe('extractSessionId', () => {
9
- it('should extract UUID session ID from output', () => {
10
- const output =
11
- 'Some text with session ID: 0199e659-0e5f-7843-8876-5a65c64e77c0 in it';
12
- const sessionId = extractSessionId(output);
13
- expect(sessionId).toBe('0199e659-0e5f-7843-8876-5a65c64e77c0');
14
- });
15
-
16
- it('should return null if no session ID found', () => {
17
- const output = 'Some text without session ID';
18
- const sessionId = extractSessionId(output);
19
- expect(sessionId).toBe(null);
20
- });
21
-
22
- it('should handle uppercase UUIDs', () => {
23
- const output = 'Session: 0199E659-0E5F-7843-8876-5A65C64E77C0';
24
- const sessionId = extractSessionId(output);
25
- expect(sessionId).toBe('0199E659-0E5F-7843-8876-5A65C64E77C0');
26
- });
27
- });
28
-
29
- describe('extractSessionIdFromSessionMeta', () => {
30
- it('should extract session ID from session metadata JSON', () => {
31
- const sessionContent = `{"timestamp":"2025-10-15T05:30:20.265Z","type":"session_meta","payload":{"id":"0199e659-0e5f-7843-8876-5a65c64e77c0","timestamp":"2025-10-15T05:30:20.127Z","cwd":"/some/path"}}
32
- {"timestamp":"2025-10-15T05:30:20.415Z","type":"response_item","payload":{"type":"message","role":"user"}}`;
33
-
34
- const sessionId = extractSessionIdFromSessionMeta(sessionContent);
35
- expect(sessionId).toBe('0199e659-0e5f-7843-8876-5a65c64e77c0');
36
- });
37
-
38
- it('should fall back to regex extraction if JSON parsing fails', () => {
39
- const sessionContent =
40
- 'Invalid JSON but contains 0199e659-0e5f-7843-8876-5a65c64e77c0';
41
- const sessionId = extractSessionIdFromSessionMeta(sessionContent);
42
- expect(sessionId).toBe('0199e659-0e5f-7843-8876-5a65c64e77c0');
43
- });
44
-
45
- it('should return null if no session ID found', () => {
46
- const sessionContent = 'No session ID here';
47
- const sessionId = extractSessionIdFromSessionMeta(sessionContent);
48
- expect(sessionId).toBe(null);
49
- });
50
- });
51
- });
@@ -1,259 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
2
- import { mkdir, rm, writeFile } from 'fs/promises';
3
- import { tmpdir } from 'os';
4
- import { join } from 'path';
5
- import {
6
- type CodexSession,
7
- extractSessionId,
8
- extractSessionIdFromSessionMeta,
9
- getAllWorkingDirectories,
10
- getRecentSessionsForCwd,
11
- getSessionForCwd,
12
- storeSessionForCwd,
13
- } from './codexSessionManager';
14
-
15
- // Create a temporary test directory
16
- const testDir = join(tmpdir(), 'claude-yes-test-' + Date.now());
17
- const testCodexDir = join(testDir, '.codex', 'sessions');
18
- const testConfigDir = join(testDir, '.config', 'cli-yes');
19
-
20
- // Store original environment
21
- const originalTestHome = process.env.CLI_YES_TEST_HOME;
22
-
23
- beforeEach(async () => {
24
- // Set up test directories
25
- await mkdir(testCodexDir, { recursive: true });
26
- await mkdir(testConfigDir, { recursive: true });
27
-
28
- // Set test home directory
29
- process.env.CLI_YES_TEST_HOME = testDir;
30
- });
31
-
32
- afterEach(async () => {
33
- // Clean up
34
- process.env.CLI_YES_TEST_HOME = originalTestHome;
35
- await rm(testDir, { recursive: true, force: true });
36
- });
37
-
38
- // Helper function to create a mock codex session file
39
- async function createMockSessionFile(sessionData: {
40
- id: string;
41
- timestamp: string;
42
- cwd: string;
43
- git?: any;
44
- }) {
45
- const year = new Date(sessionData.timestamp).getFullYear();
46
- const month = String(new Date(sessionData.timestamp).getMonth() + 1).padStart(
47
- 2,
48
- '0',
49
- );
50
- const day = String(new Date(sessionData.timestamp).getDate()).padStart(
51
- 2,
52
- '0',
53
- );
54
-
55
- const sessionDir = join(testCodexDir, String(year), month, day);
56
- await mkdir(sessionDir, { recursive: true });
57
-
58
- const filename = `test-session-${sessionData.id}.jsonl`;
59
- const filePath = join(sessionDir, filename);
60
-
61
- const sessionMeta = {
62
- timestamp: sessionData.timestamp,
63
- type: 'session_meta',
64
- payload: {
65
- id: sessionData.id,
66
- timestamp: sessionData.timestamp,
67
- cwd: sessionData.cwd,
68
- originator: 'codex_cli_rs',
69
- cli_version: '0.42.0',
70
- instructions: null,
71
- git: sessionData.git,
72
- },
73
- };
74
-
75
- const content = JSON.stringify(sessionMeta) + '\n';
76
- await writeFile(filePath, content);
77
-
78
- return filePath;
79
- }
80
-
81
- describe('codexSessionManager', () => {
82
- describe('extractSessionId', () => {
83
- it('should extract valid session IDs from output', () => {
84
- const output1 = 'Session ID: 019a4877-5f3c-7763-b573-513cc2d5d291';
85
- const output2 =
86
- 'Starting session 019a4877-5f3c-7763-b573-513cc2d5d291 for user';
87
- const output3 = 'No session ID here';
88
-
89
- expect(extractSessionId(output1)).toBe(
90
- '019a4877-5f3c-7763-b573-513cc2d5d291',
91
- );
92
- expect(extractSessionId(output2)).toBe(
93
- '019a4877-5f3c-7763-b573-513cc2d5d291',
94
- );
95
- expect(extractSessionId(output3)).toBeNull();
96
- });
97
- });
98
-
99
- describe('extractSessionIdFromSessionMeta', () => {
100
- it('should extract session ID from valid session metadata', () => {
101
- const sessionContent = JSON.stringify({
102
- timestamp: '2025-11-03T06:46:14.123Z',
103
- type: 'session_meta',
104
- payload: {
105
- id: '019a4877-5f3c-7763-b573-513cc2d5d291',
106
- cwd: '/test/path',
107
- },
108
- });
109
-
110
- expect(extractSessionIdFromSessionMeta(sessionContent)).toBe(
111
- '019a4877-5f3c-7763-b573-513cc2d5d291',
112
- );
113
- });
114
-
115
- it('should fall back to regex extraction for invalid JSON', () => {
116
- const invalidContent =
117
- 'Invalid JSON but contains 019a4877-5f3c-7763-b573-513cc2d5d291';
118
-
119
- expect(extractSessionIdFromSessionMeta(invalidContent)).toBe(
120
- '019a4877-5f3c-7763-b573-513cc2d5d291',
121
- );
122
- });
123
- });
124
-
125
- describe('session storage and retrieval', () => {
126
- it('should store and retrieve session IDs for directories', async () => {
127
- const cwd = '/test/project';
128
- const sessionId = '019a4877-5f3c-7763-b573-513cc2d5d291';
129
-
130
- await storeSessionForCwd(cwd, sessionId);
131
- const retrieved = await getSessionForCwd(cwd);
132
-
133
- expect(retrieved).toBe(sessionId);
134
- });
135
-
136
- it('should return null for non-existent directories', async () => {
137
- const result = await getSessionForCwd('/non/existent');
138
- expect(result).toBeNull();
139
- });
140
- });
141
-
142
- describe('codex session file parsing', () => {
143
- it('should read sessions from actual codex files', async () => {
144
- const sessionData = {
145
- id: '019a4877-5f3c-7763-b573-513cc2d5d291',
146
- timestamp: '2025-11-03T06:46:14.123Z',
147
- cwd: '/v1/code/snomiao/claude-yes/tree/main',
148
- git: {
149
- commit_hash: 'abc123',
150
- branch: 'main',
151
- repository_url: 'git@github.com:snomiao/claude-yes.git',
152
- },
153
- };
154
-
155
- await createMockSessionFile(sessionData);
156
-
157
- const retrieved = await getSessionForCwd(sessionData.cwd);
158
- expect(retrieved).toBe(sessionData.id);
159
- });
160
-
161
- it('should get recent sessions for a directory', async () => {
162
- const cwd = '/test/project';
163
- const sessions = [
164
- {
165
- id: 'session-1',
166
- timestamp: '2025-11-03T10:00:00.000Z',
167
- cwd,
168
- },
169
- {
170
- id: 'session-2',
171
- timestamp: '2025-11-03T09:00:00.000Z',
172
- cwd,
173
- },
174
- {
175
- id: 'session-3',
176
- timestamp: '2025-11-03T08:00:00.000Z',
177
- cwd,
178
- },
179
- ];
180
-
181
- for (const session of sessions) {
182
- await createMockSessionFile(session);
183
- }
184
-
185
- const recent = await getRecentSessionsForCwd(cwd, 2);
186
- expect(recent).toHaveLength(2);
187
- expect(recent[0].id).toBe('session-1'); // Most recent first
188
- expect(recent[1].id).toBe('session-2');
189
- });
190
-
191
- it('should get all working directories with counts', async () => {
192
- const sessions = [
193
- {
194
- id: 'session-1',
195
- timestamp: '2025-11-03T10:00:00.000Z',
196
- cwd: '/project-a',
197
- },
198
- {
199
- id: 'session-2',
200
- timestamp: '2025-11-03T09:00:00.000Z',
201
- cwd: '/project-a',
202
- },
203
- {
204
- id: 'session-3',
205
- timestamp: '2025-11-03T08:00:00.000Z',
206
- cwd: '/project-b',
207
- },
208
- ];
209
-
210
- for (const session of sessions) {
211
- await createMockSessionFile(session);
212
- }
213
-
214
- const directories = await getAllWorkingDirectories();
215
- expect(directories).toHaveLength(2);
216
-
217
- const projectA = directories.find((d) => d.cwd === '/project-a');
218
- const projectB = directories.find((d) => d.cwd === '/project-b');
219
-
220
- expect(projectA?.count).toBe(2);
221
- expect(projectB?.count).toBe(1);
222
-
223
- // Should be sorted by last session time (most recent first)
224
- expect(directories[0].cwd).toBe('/project-a');
225
- });
226
- });
227
-
228
- describe('fallback behavior', () => {
229
- it('should fall back to stored mapping when no codex files exist', async () => {
230
- const cwd = '/fallback/test';
231
- const sessionId = 'fallback-session-id';
232
-
233
- // Store in mapping but don't create codex file
234
- await storeSessionForCwd(cwd, sessionId);
235
-
236
- const retrieved = await getSessionForCwd(cwd);
237
- expect(retrieved).toBe(sessionId);
238
- });
239
-
240
- it('should prefer codex files over stored mapping', async () => {
241
- const cwd = '/preference/test';
242
- const storedSessionId = 'stored-session';
243
- const codexSessionId = 'codex-session';
244
-
245
- // Store in mapping first
246
- await storeSessionForCwd(cwd, storedSessionId);
247
-
248
- // Create codex file with different session ID
249
- await createMockSessionFile({
250
- id: codexSessionId,
251
- timestamp: '2025-11-03T10:00:00.000Z',
252
- cwd,
253
- });
254
-
255
- const retrieved = await getSessionForCwd(cwd);
256
- expect(retrieved).toBe(codexSessionId); // Should prefer codex file
257
- });
258
- });
259
- });