claude-yes 1.30.0 → 1.31.0

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.
package/dist/qwen-yes.js CHANGED
@@ -14017,12 +14017,12 @@ function catcher(catchFn, fn) {
14017
14017
  }
14018
14018
 
14019
14019
  // ts/codexSessionManager.ts
14020
- import { mkdir, readFile, writeFile } from "fs/promises";
14020
+ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
14021
14021
  import { homedir } from "os";
14022
14022
  import path7 from "path";
14023
14023
  async function loadSessionMap() {
14024
14024
  try {
14025
- const content = await readFile(SESSIONS_FILE, "utf-8");
14025
+ const content = await readFile(getSessionsFile(), "utf-8");
14026
14026
  return JSON.parse(content);
14027
14027
  } catch (error) {
14028
14028
  return {};
@@ -14030,8 +14030,9 @@ async function loadSessionMap() {
14030
14030
  }
14031
14031
  async function saveSessionMap(sessionMap) {
14032
14032
  try {
14033
- await mkdir(path7.dirname(SESSIONS_FILE), { recursive: true });
14034
- await writeFile(SESSIONS_FILE, JSON.stringify(sessionMap, null, 2));
14033
+ const sessionsFile = getSessionsFile();
14034
+ await mkdir(path7.dirname(sessionsFile), { recursive: true });
14035
+ await writeFile(sessionsFile, JSON.stringify(sessionMap, null, 2));
14035
14036
  } catch (error) {
14036
14037
  console.warn("Failed to save codex session map:", error);
14037
14038
  }
@@ -14044,7 +14045,78 @@ async function storeSessionForCwd(cwd, sessionId) {
14044
14045
  };
14045
14046
  await saveSessionMap(sessionMap);
14046
14047
  }
14048
+ async function parseCodexSessionFile(filePath) {
14049
+ try {
14050
+ const content = await readFile(filePath, "utf-8");
14051
+ const lines2 = content.trim().split(`
14052
+ `);
14053
+ for (const line of lines2) {
14054
+ if (!line.trim())
14055
+ continue;
14056
+ const data = JSON.parse(line);
14057
+ if (data.type === "session_meta" && data.payload) {
14058
+ const payload = data.payload;
14059
+ return {
14060
+ id: payload.id,
14061
+ timestamp: payload.timestamp || data.timestamp,
14062
+ cwd: payload.cwd,
14063
+ filePath,
14064
+ git: payload.git
14065
+ };
14066
+ }
14067
+ }
14068
+ return null;
14069
+ } catch (error) {
14070
+ return null;
14071
+ }
14072
+ }
14073
+ async function getAllCodexSessions() {
14074
+ const sessions = [];
14075
+ const codexSessionsDir = getCodexSessionsDir();
14076
+ try {
14077
+ const years = await readdir(codexSessionsDir);
14078
+ for (const year of years) {
14079
+ const yearPath = path7.join(codexSessionsDir, year);
14080
+ try {
14081
+ const months = await readdir(yearPath);
14082
+ for (const month of months) {
14083
+ const monthPath = path7.join(yearPath, month);
14084
+ try {
14085
+ const days = await readdir(monthPath);
14086
+ for (const day of days) {
14087
+ const dayPath = path7.join(monthPath, day);
14088
+ try {
14089
+ const files = await readdir(dayPath);
14090
+ for (const file of files) {
14091
+ if (file.endsWith(".jsonl")) {
14092
+ const sessionPath = path7.join(dayPath, file);
14093
+ const session = await parseCodexSessionFile(sessionPath);
14094
+ if (session) {
14095
+ sessions.push(session);
14096
+ }
14097
+ }
14098
+ }
14099
+ } catch (error) {}
14100
+ }
14101
+ } catch (error) {}
14102
+ }
14103
+ } catch (error) {}
14104
+ }
14105
+ } catch (error) {
14106
+ return [];
14107
+ }
14108
+ return sessions.sort((a2, b) => new Date(b.timestamp).getTime() - new Date(a2.timestamp).getTime());
14109
+ }
14110
+ async function getMostRecentCodexSessionForCwd(targetCwd) {
14111
+ const allSessions = await getAllCodexSessions();
14112
+ const sessionsForCwd = allSessions.filter((session) => session.cwd === targetCwd);
14113
+ return sessionsForCwd[0] || null;
14114
+ }
14047
14115
  async function getSessionForCwd(cwd) {
14116
+ const recentSession = await getMostRecentCodexSessionForCwd(cwd);
14117
+ if (recentSession) {
14118
+ return recentSession.id;
14119
+ }
14048
14120
  const sessionMap = await loadSessionMap();
14049
14121
  return sessionMap[cwd]?.sessionId || null;
14050
14122
  }
@@ -14053,10 +14125,8 @@ function extractSessionId(output) {
14053
14125
  const match = output.match(sessionIdRegex);
14054
14126
  return match ? match[0] : null;
14055
14127
  }
14056
- var SESSIONS_FILE;
14057
- var init_codexSessionManager = __esm(() => {
14058
- SESSIONS_FILE = path7.join(homedir(), ".config", "cli-yes", "codex-sessions.json");
14059
- });
14128
+ var getSessionsFile = () => process.env.CLI_YES_TEST_HOME ? path7.join(process.env.CLI_YES_TEST_HOME, ".config", "cli-yes", "codex-sessions.json") : path7.join(homedir(), ".config", "cli-yes", "codex-sessions.json"), getCodexSessionsDir = () => process.env.CLI_YES_TEST_HOME ? path7.join(process.env.CLI_YES_TEST_HOME, ".codex", "sessions") : path7.join(homedir(), ".codex", "sessions");
14129
+ var init_codexSessionManager = () => {};
14060
14130
 
14061
14131
  // ts/idleWaiter.ts
14062
14132
  class IdleWaiter {
@@ -20339,8 +20409,9 @@ var init_ts = __esm(async () => {
20339
20409
  // ts/cli.ts
20340
20410
  init_dist();
20341
20411
  init_cli_yes_config();
20342
- if (!globalThis.Bun) {
20343
- console.log("Re-running with Bun...", process.argv);
20412
+ var hasNodePty = !!await import("node-pty").catch(() => null);
20413
+ if (!globalThis.Bun && !hasNodePty) {
20414
+ console.log("No node-pty installed. Re-running with Bun...", process.argv);
20344
20415
  (await import("child_process")).spawnSync("node_modules/.bin/bun", [process.argv[1], "--", ...process.argv.slice(2)], { stdio: "inherit" });
20345
20416
  process.exit(0);
20346
20417
  }
@@ -20357,5 +20428,5 @@ if (config2.verbose) {
20357
20428
  var { exitCode } = await cliYes2(config2);
20358
20429
  process.exit(exitCode ?? 1);
20359
20430
 
20360
- //# debugId=A6B971C2331DA0F864756E2164756E21
20431
+ //# debugId=229819D0379CAA1264756E2164756E21
20361
20432
  //# sourceMappingURL=cli.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.30.0",
3
+ "version": "1.31.0",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "claude",
@@ -91,11 +91,9 @@
91
91
  ]
92
92
  },
93
93
  "dependencies": {
94
- "@types/ms": "^2.1.0",
95
94
  "bun": "^1.3.1",
96
95
  "bun-pty": "^0.3.2",
97
- "from-node-stream": "^0.0.11",
98
- "ms": "^2.1.3"
96
+ "from-node-stream": "^0.0.11"
99
97
  },
100
98
  "devDependencies": {
101
99
  "@biomejs/biome": "^2.2.5",
@@ -105,12 +103,14 @@
105
103
  "@semantic-release/release-notes-generator": "^14.1.0",
106
104
  "@types/bun": "^1.2.18",
107
105
  "@types/jest": "^30.0.0",
106
+ "@types/ms": "^2.1.0",
108
107
  "@types/node": "^24.0.10",
109
108
  "@types/yargs": "^17.0.33",
110
109
  "cpu-wait": "^0.0.10",
111
110
  "execa": "^9.6.0",
112
111
  "husky": "^9.1.7",
113
112
  "lint-staged": "^16.1.4",
113
+ "ms": "^2.1.3",
114
114
  "p-map": "^7.0.3",
115
115
  "phpdie": "^1.7.0",
116
116
  "rambda": "^10.3.2",
@@ -125,5 +125,10 @@
125
125
  "peerDependencies": {
126
126
  "node-pty": "^1.1.0-beta38",
127
127
  "typescript": "^5.8.3"
128
+ },
129
+ "peerDependenciesMeta": {
130
+ "node-pty": {
131
+ "optional": true
132
+ }
128
133
  }
129
134
  }
package/ts/cli.ts CHANGED
@@ -3,10 +3,10 @@ import DIE from 'phpdie';
3
3
  import cliYesConfig from '../cli-yes.config';
4
4
 
5
5
  // if node-pty is not installed, re-run with bun
6
- // const hasNodePty = !!await import('node-pty').catch(() => null);
7
- if (!globalThis.Bun) {
6
+ const hasNodePty = !!(await import('node-pty').catch(() => null));
7
+ if (!globalThis.Bun && !hasNodePty) {
8
8
  // run with same arguments in Bun if not already
9
- console.log('Re-running with Bun...', process.argv);
9
+ console.log('No node-pty installed. Re-running with Bun...', process.argv);
10
10
  (await import('child_process')).spawnSync(
11
11
  'node_modules/.bin/bun',
12
12
  [process.argv[1]!, '--', ...process.argv.slice(2)],
@@ -68,10 +68,6 @@ describe('Codex Session Restoration', () => {
68
68
  // Create test directories
69
69
  await mkdir(cwd1, { recursive: true });
70
70
  await mkdir(cwd2, { recursive: true });
71
-
72
- // Build the project first
73
- const buildResult = await runCodexYes(['--help'], process.cwd(), 10000);
74
- console.log('Build check:', buildResult.exitCode === 0 ? 'OK' : 'FAILED');
75
71
  });
76
72
 
77
73
  afterAll(async () => {
@@ -0,0 +1,259 @@
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
+ });