coding-tool-x 3.6.12 → 3.6.13

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.
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>coding-tool-x · AI 编程工作台</title>
8
- <script type="module" crossorigin src="/assets/index-y-eEayxR.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-BpBFF3b2.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/markdown-BNJfEgCA.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vue-vendor-B5RqjEjQ.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/naive-ui-kcWDeJwl.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-tool-x",
3
- "version": "3.6.12",
3
+ "version": "3.6.13",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -2,7 +2,6 @@
2
2
  const chalk = require('chalk');
3
3
  const inquirer = require('inquirer');
4
4
  const { loadConfig, saveConfig } = require('../config/loader');
5
- const { HOME_DIR } = require('../config/paths');
6
5
 
7
6
  /**
8
7
  * 配置端口
@@ -112,7 +111,6 @@ async function handlePortConfig() {
112
111
  // 保存配置(保留其余字段)
113
112
  saveConfig({
114
113
  ...config,
115
- projectsDir: config.projectsDir.replace(HOME_DIR, '~'),
116
114
  ports: config.ports,
117
115
  });
118
116
 
@@ -3,7 +3,6 @@ const chalk = require('chalk');
3
3
  const { getAvailableProjects } = require('../utils/session');
4
4
  const { promptSelectProject } = require('../ui/prompts');
5
5
  const { saveConfig } = require('../config/loader');
6
- const { HOME_DIR } = require('../config/paths');
7
6
 
8
7
  /**
9
8
  * 切换项目
@@ -29,10 +28,7 @@ async function switchProject(config) {
29
28
  config.defaultProject = selectedProject;
30
29
 
31
30
  // 保存到配置文件(保留其余字段)
32
- saveConfig({
33
- ...config,
34
- projectsDir: config.projectsDir.replace(HOME_DIR, '~')
35
- });
31
+ saveConfig({ ...config });
36
32
 
37
33
  // 使用解析后的名称显示
38
34
  const { parseRealProjectPath } = require('../server/services/sessions');
@@ -188,6 +188,8 @@ async function createWorkspace() {
188
188
 
189
189
  let createWorktree = false;
190
190
  let branch = '';
191
+ let branchMode = 'existing';
192
+ let baseBranch = '';
191
193
 
192
194
  if (isGit) {
193
195
  const { shouldCreateWorktree } = await inquirer.prompt([
@@ -200,11 +202,24 @@ async function createWorkspace() {
200
202
  ]);
201
203
 
202
204
  if (shouldCreateWorktree) {
205
+ const { selectedBranchMode } = await inquirer.prompt([
206
+ {
207
+ type: 'list',
208
+ name: 'selectedBranchMode',
209
+ message: '选择 worktree 分支模式:',
210
+ choices: [
211
+ { name: '使用已有分支', value: 'existing' },
212
+ { name: '新建分支', value: 'new' }
213
+ ],
214
+ default: 'existing'
215
+ }
216
+ ]);
217
+
203
218
  const { branchName } = await inquirer.prompt([
204
219
  {
205
220
  type: 'input',
206
221
  name: 'branchName',
207
- message: '输入分支名:',
222
+ message: selectedBranchMode === 'new' ? '输入新分支名:' : '输入已有分支名:',
208
223
  validate: input => {
209
224
  if (!input || !input.trim()) {
210
225
  return '分支名不能为空';
@@ -214,7 +229,20 @@ async function createWorkspace() {
214
229
  }
215
230
  ]);
216
231
 
232
+ if (selectedBranchMode === 'new') {
233
+ const { baseBranchName } = await inquirer.prompt([
234
+ {
235
+ type: 'input',
236
+ name: 'baseBranchName',
237
+ message: '输入基础分支(可选,如 main):',
238
+ default: ''
239
+ }
240
+ ]);
241
+ baseBranch = baseBranchName;
242
+ }
243
+
217
244
  createWorktree = true;
245
+ branchMode = selectedBranchMode;
218
246
  branch = branchName;
219
247
  }
220
248
  }
@@ -223,7 +251,9 @@ async function createWorkspace() {
223
251
  sourcePath,
224
252
  name: linkName,
225
253
  createWorktree,
226
- branch
254
+ branch,
255
+ branchMode,
256
+ baseBranch
227
257
  });
228
258
 
229
259
  console.log(chalk.green(`\n[v] 已添加: ${linkName}\n`));
@@ -245,7 +275,9 @@ async function createWorkspace() {
245
275
  projects.forEach((proj, index) => {
246
276
  console.log(chalk.gray(` ${index + 1}. ${proj.name} → ${proj.sourcePath}`));
247
277
  if (proj.createWorktree) {
248
- console.log(chalk.gray(` (创建 worktree: ${proj.branch})`));
278
+ const modeLabel = proj.branchMode === 'new' ? '新建分支' : '已有分支';
279
+ const baseLabel = proj.branchMode === 'new' && proj.baseBranch ? `,基于 ${proj.baseBranch}` : '';
280
+ console.log(chalk.gray(` (创建 worktree: ${modeLabel} ${proj.branch}${baseLabel})`));
249
281
  }
250
282
  });
251
283
  console.log('');
@@ -1,13 +1,7 @@
1
1
  // 默认配置
2
- const path = require('path');
3
- const os = require('os');
4
- const { resolvePreferredHomeDir } = require('../utils/home-dir');
5
2
  const modelMetadataConfig = require('./model-metadata.json');
6
3
 
7
- const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
8
-
9
4
  const DEFAULT_CONFIG = {
10
- projectsDir: path.join(HOME_DIR, '.claude', 'projects'),
11
5
  defaultProject: null,
12
6
  maxDisplaySessions: 100,
13
7
  pageSize: 15,
@@ -3,7 +3,7 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const os = require('os');
5
5
  const DEFAULT_CONFIG = require('./default');
6
- const { PATHS, ensureStorageDirMigrated } = require('./paths');
6
+ const { PATHS, NATIVE_PATHS, ensureStorageDirMigrated } = require('./paths');
7
7
  const { resolvePreferredHomeDir } = require('../utils/home-dir');
8
8
  const eventBus = require('../plugins/event-bus');
9
9
 
@@ -23,12 +23,79 @@ function getConfigFilePath() {
23
23
  * 展开 ~ 为用户主目录
24
24
  */
25
25
  function expandHome(filepath) {
26
+ if (typeof filepath !== 'string') {
27
+ return filepath;
28
+ }
26
29
  if (filepath.startsWith('~')) {
27
30
  return path.join(HOME_DIR, filepath.slice(1));
28
31
  }
29
32
  return filepath;
30
33
  }
31
34
 
35
+ function collapseHome(filepath) {
36
+ if (typeof filepath !== 'string') {
37
+ return filepath;
38
+ }
39
+ if (filepath === HOME_DIR) {
40
+ return '~';
41
+ }
42
+ const prefix = `${HOME_DIR}${path.sep}`;
43
+ if (filepath.startsWith(prefix)) {
44
+ return path.join('~', filepath.slice(prefix.length));
45
+ }
46
+ return filepath;
47
+ }
48
+
49
+ function getNativeClaudeProjectsDir() {
50
+ return NATIVE_PATHS?.claude?.projects || path.join(HOME_DIR, '.claude', 'projects');
51
+ }
52
+
53
+ function hasExplicitProjectsDir(config = {}) {
54
+ return Object.prototype.hasOwnProperty.call(config, 'projectsDir') &&
55
+ typeof config.projectsDir === 'string' &&
56
+ config.projectsDir.trim() !== '';
57
+ }
58
+
59
+ function resolveClaudeProjectsDir(config = {}) {
60
+ if (!hasExplicitProjectsDir(config) || isDefaultProjectsDir(config.projectsDir)) {
61
+ return getNativeClaudeProjectsDir();
62
+ }
63
+ return expandHome(config.projectsDir);
64
+ }
65
+
66
+ function normalizeComparablePath(filepath) {
67
+ if (typeof filepath !== 'string' || !filepath.trim()) {
68
+ return '';
69
+ }
70
+ return path.normalize(expandHome(filepath.trim()));
71
+ }
72
+
73
+ function isDefaultProjectsDir(projectsDir) {
74
+ const normalized = normalizeComparablePath(projectsDir);
75
+ if (!normalized) {
76
+ return true;
77
+ }
78
+ const nativeDefault = normalizeComparablePath(getNativeClaudeProjectsDir());
79
+ const legacyHomeDefault = normalizeComparablePath(path.join(HOME_DIR, '.claude', 'projects'));
80
+ if (normalized === nativeDefault || normalized === legacyHomeDefault) {
81
+ return true;
82
+ }
83
+ const normalizedParts = normalized.replace(/\\/g, '/').split('/').filter(Boolean);
84
+ const last = normalizedParts[normalizedParts.length - 1];
85
+ const previous = normalizedParts[normalizedParts.length - 2];
86
+ return previous === '.claude' && last === 'projects' && !fs.existsSync(normalized);
87
+ }
88
+
89
+ function normalizeConfigForSave(config = {}) {
90
+ const normalized = { ...config };
91
+ if (isDefaultProjectsDir(normalized.projectsDir)) {
92
+ delete normalized.projectsDir;
93
+ } else if (typeof normalized.projectsDir === 'string') {
94
+ normalized.projectsDir = collapseHome(normalized.projectsDir);
95
+ }
96
+ return normalized;
97
+ }
98
+
32
99
  function mergePricing(defaultPricing, overrides = {}) {
33
100
  const merged = {};
34
101
  Object.keys(defaultPricing).forEach((key) => {
@@ -96,7 +163,7 @@ function loadConfig() {
96
163
  if (fs.existsSync(configFilePath)) {
97
164
  const userConfig = readJsonFile(configFilePath);
98
165
  const config = { ...DEFAULT_CONFIG, ...userConfig };
99
- config.projectsDir = expandHome(config.projectsDir || DEFAULT_CONFIG.projectsDir);
166
+ config.projectsDir = resolveClaudeProjectsDir(userConfig);
100
167
 
101
168
  // 合并 ports 配置
102
169
  config.ports = { ...DEFAULT_CONFIG.ports, ...userConfig.ports };
@@ -120,7 +187,7 @@ function loadConfig() {
120
187
  }
121
188
  const defaultConfig = {
122
189
  ...DEFAULT_CONFIG,
123
- projectsDir: expandHome(DEFAULT_CONFIG.projectsDir),
190
+ projectsDir: resolveClaudeProjectsDir(),
124
191
  currentProject: DEFAULT_CONFIG.defaultProject
125
192
  };
126
193
  eventBus.emitSync('config:loaded', { config: defaultConfig });
@@ -137,8 +204,9 @@ function saveConfig(config) {
137
204
  if (!fs.existsSync(dir)) {
138
205
  fs.mkdirSync(dir, { recursive: true });
139
206
  }
140
- fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2), 'utf8');
141
- eventBus.emitSync('config:saved', { config });
207
+ const normalizedConfig = normalizeConfigForSave(config);
208
+ fs.writeFileSync(configFilePath, JSON.stringify(normalizedConfig, null, 2), 'utf8');
209
+ eventBus.emitSync('config:saved', { config: normalizedConfig });
142
210
  } catch (error) {
143
211
  console.error(`保存配置失败 (${configFilePath}):`, error.message);
144
212
  }
@@ -148,5 +216,8 @@ module.exports = {
148
216
  loadConfig,
149
217
  saveConfig,
150
218
  expandHome,
219
+ resolveClaudeProjectsDir,
220
+ normalizeConfigForSave,
221
+ isDefaultProjectsDir,
151
222
  getConfigFilePath
152
223
  };
@@ -330,7 +330,6 @@ router.post('/default-models', (req, res) => {
330
330
  // Save config
331
331
  const newConfig = {
332
332
  ...config,
333
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
334
333
  defaultModels: newDefaultModels
335
334
  };
336
335
 
@@ -378,7 +377,6 @@ router.post('/default-models/reset', (req, res) => {
378
377
  // Save config
379
378
  const newConfig = {
380
379
  ...config,
381
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
382
380
  defaultModels: newDefaultModels
383
381
  };
384
382
 
@@ -489,7 +487,6 @@ router.post('/advanced', (req, res) => {
489
487
  // 更新配置
490
488
  const newConfig = {
491
489
  ...config,
492
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
493
490
  ports: normalizedPorts,
494
491
  maxLogs: maxLogs !== undefined ? parseInt(maxLogs) : config.maxLogs,
495
492
  statsInterval: statsInterval !== undefined ? parseInt(statsInterval) : config.statsInterval,
@@ -90,7 +90,6 @@ function handleSaveModelSettings(req, res) {
90
90
  const config = loadConfig();
91
91
  const newConfig = {
92
92
  ...config,
93
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
94
93
  modelMetadataOverrides: overrides && typeof overrides === 'object'
95
94
  ? overrides
96
95
  : (config.modelMetadataOverrides || {})
@@ -124,7 +123,6 @@ function handleDeleteModelOverride(req, res) {
124
123
 
125
124
  const newConfig = {
126
125
  ...config,
127
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
128
126
  modelMetadataOverrides: overrides
129
127
  };
130
128
  saveConfig(newConfig);
@@ -33,6 +33,24 @@ function validateBranchName(branchName) {
33
33
  }
34
34
  }
35
35
 
36
+ function normalizeBranchMode(branchMode) {
37
+ if (typeof branchMode !== 'string' || !branchMode.trim()) {
38
+ return '';
39
+ }
40
+ return branchMode.trim();
41
+ }
42
+
43
+ function validateBranchMode(branchMode) {
44
+ const normalized = normalizeBranchMode(branchMode);
45
+ if (!normalized) {
46
+ return { valid: true, normalized: '' };
47
+ }
48
+ if (normalized !== 'existing' && normalized !== 'new') {
49
+ return { valid: false, normalized, message: `分支模式不合法: ${normalized}` };
50
+ }
51
+ return { valid: true, normalized };
52
+ }
53
+
36
54
  /**
37
55
  * GET /api/workspaces
38
56
  * 获取所有工作区列表
@@ -226,6 +244,17 @@ router.post('/', (req, res) => {
226
244
  });
227
245
  }
228
246
 
247
+ const branchModeValidation = validateBranchMode(proj.branchMode);
248
+ if (!branchModeValidation.valid) {
249
+ return res.status(400).json({
250
+ success: false,
251
+ message: branchModeValidation.message
252
+ });
253
+ }
254
+ if (branchModeValidation.normalized) {
255
+ proj.branchMode = branchModeValidation.normalized;
256
+ }
257
+
229
258
  const normalizedBranch = normalizeBranchName(proj.branch);
230
259
  if (normalizedBranch) {
231
260
  const branchValidation = validateBranchName(normalizedBranch);
@@ -329,9 +358,10 @@ router.put('/:id/last-used', (req, res) => {
329
358
  router.post('/:id/projects', (req, res) => {
330
359
  try {
331
360
  const { id } = req.params;
332
- const { sourcePath, name, createWorktree, branch, baseBranch } = req.body;
361
+ const { sourcePath, name, createWorktree, branch, baseBranch, branchMode } = req.body;
333
362
  const normalizedBranch = normalizeBranchName(branch);
334
363
  const normalizedBaseBranch = normalizeBranchName(baseBranch);
364
+ const branchModeValidation = validateBranchMode(branchMode);
335
365
 
336
366
  if (!sourcePath || !sourcePath.trim()) {
337
367
  return res.status(400).json({
@@ -340,6 +370,13 @@ router.post('/:id/projects', (req, res) => {
340
370
  });
341
371
  }
342
372
 
373
+ if (!branchModeValidation.valid) {
374
+ return res.status(400).json({
375
+ success: false,
376
+ message: branchModeValidation.message
377
+ });
378
+ }
379
+
343
380
  if (normalizedBranch) {
344
381
  const branchValidation = validateBranchName(normalizedBranch);
345
382
  if (!branchValidation.valid) {
@@ -364,6 +401,7 @@ router.post('/:id/projects', (req, res) => {
364
401
  sourcePath,
365
402
  name,
366
403
  createWorktree,
404
+ branchMode: branchModeValidation.normalized || branchMode,
367
405
  branch: normalizedBranch || branch,
368
406
  baseBranch: normalizedBaseBranch || baseBranch
369
407
  });
@@ -17,6 +17,17 @@ const CLAUDE_PROJECTS_DIR = NATIVE_PATHS.claude.projects;
17
17
  const CODEX_PROJECTS_DIR = path.join(path.dirname(NATIVE_PATHS.codex.config), 'projects');
18
18
  const GEMINI_PROJECTS_DIR = path.join(path.dirname(NATIVE_PATHS.gemini.env), 'projects');
19
19
 
20
+ function resolveProjectsDir(config = {}) {
21
+ return config.projectsDir || CLAUDE_PROJECTS_DIR;
22
+ }
23
+
24
+ function withResolvedProjectsDir(config = {}) {
25
+ return {
26
+ ...config,
27
+ projectsDir: resolveProjectsDir(config)
28
+ };
29
+ }
30
+
20
31
  // Base directory for cc-tool data
21
32
  function getCcToolDir() {
22
33
  return PATHS.base;
@@ -182,7 +193,7 @@ function sliceClaudeSessionContentByUserMessage(content, afterUserMessageNumber)
182
193
 
183
194
  // Get all projects with stats (async version)
184
195
  async function getProjects(config) {
185
- const projectsDir = config.projectsDir;
196
+ const projectsDir = resolveProjectsDir(config);
186
197
 
187
198
  if (!fs.existsSync(projectsDir)) {
188
199
  return [];
@@ -383,16 +394,17 @@ function extractCwdFromSessionHeader(sessionFile) {
383
394
 
384
395
  // Get projects with detailed stats (with caching) - async version
385
396
  async function getProjectsWithStats(config, options = {}) {
397
+ const resolvedConfig = withResolvedProjectsDir(config);
386
398
  if (!options.force) {
387
399
  // Check enhanced cache first
388
- const cacheKey = `${CacheKeys.PROJECTS}${config.projectsDir}`;
400
+ const cacheKey = `${CacheKeys.PROJECTS}${resolvedConfig.projectsDir}`;
389
401
  const enhancedCached = globalCache.get(cacheKey);
390
402
  if (enhancedCached) {
391
403
  return enhancedCached;
392
404
  }
393
405
 
394
406
  // Check old cache
395
- const cached = getCachedProjects(config);
407
+ const cached = getCachedProjects(resolvedConfig);
396
408
  if (cached) {
397
409
  globalCache.set(cacheKey, cached, 300000); // 5分钟
398
410
  return cached;
@@ -400,22 +412,22 @@ async function getProjectsWithStats(config, options = {}) {
400
412
  }
401
413
 
402
414
  try {
403
- const data = await buildProjectsWithStats(config);
415
+ const data = await buildProjectsWithStats(resolvedConfig);
404
416
  if (!Array.isArray(data)) {
405
- console.warn(`[getProjectsWithStats] Unexpected non-array result for ${config.projectsDir}, returning empty array.`);
417
+ console.warn(`[getProjectsWithStats] Unexpected non-array result for ${resolvedConfig.projectsDir}, returning empty array.`);
406
418
  return [];
407
419
  }
408
- setCachedProjects(config, data);
409
- globalCache.set(`${CacheKeys.PROJECTS}${config.projectsDir}`, data, 300000);
420
+ setCachedProjects(resolvedConfig, data);
421
+ globalCache.set(`${CacheKeys.PROJECTS}${resolvedConfig.projectsDir}`, data, 300000);
410
422
  return data;
411
423
  } catch (err) {
412
- console.error(`[getProjectsWithStats] Failed to build projects for ${config.projectsDir}:`, err);
424
+ console.error(`[getProjectsWithStats] Failed to build projects for ${resolvedConfig.projectsDir}:`, err);
413
425
  return [];
414
426
  }
415
427
  }
416
428
 
417
429
  async function buildProjectsWithStats(config) {
418
- const projectsDir = config.projectsDir;
430
+ const projectsDir = resolveProjectsDir(config);
419
431
 
420
432
  if (!fs.existsSync(projectsDir)) {
421
433
  return [];
@@ -482,7 +494,7 @@ async function buildProjectsWithStats(config) {
482
494
 
483
495
  // 获取 Claude 项目/会话数量(轻量统计)
484
496
  function getProjectAndSessionCounts(config) {
485
- const projectsDir = config.projectsDir;
497
+ const projectsDir = resolveProjectsDir(config);
486
498
  if (!fs.existsSync(projectsDir)) {
487
499
  return { projectCount: 0, sessionCount: 0 };
488
500
  }
@@ -670,7 +682,8 @@ async function getSessionsForProject(config, projectName) {
670
682
 
671
683
  // Delete a session
672
684
  function deleteSession(config, projectName, sessionId) {
673
- const projectDir = path.join(config.projectsDir, projectName);
685
+ const resolvedConfig = withResolvedProjectsDir(config);
686
+ const projectDir = path.join(resolvedConfig.projectsDir, projectName);
674
687
  const sessionFile = path.join(projectDir, sessionId + '.jsonl');
675
688
 
676
689
  if (!fs.existsSync(sessionFile)) {
@@ -678,14 +691,15 @@ function deleteSession(config, projectName, sessionId) {
678
691
  }
679
692
 
680
693
  fs.unlinkSync(sessionFile);
681
- invalidateProjectsCache(config);
694
+ invalidateProjectsCache(resolvedConfig);
682
695
  invalidateSessionResultCache(projectName);
683
696
  return { success: true };
684
697
  }
685
698
 
686
699
  // Fork a session
687
700
  function forkSession(config, projectName, sessionId, options = {}) {
688
- const projectDir = path.join(config.projectsDir, projectName);
701
+ const resolvedConfig = withResolvedProjectsDir(config);
702
+ const projectDir = path.join(resolvedConfig.projectsDir, projectName);
689
703
  const sessionFile = path.join(projectDir, sessionId + '.jsonl');
690
704
 
691
705
  if (!fs.existsSync(sessionFile)) {
@@ -714,7 +728,7 @@ function forkSession(config, projectName, sessionId, options = {}) {
714
728
  const { setAlias } = require('./alias');
715
729
  setAlias(newSessionId, options.alias);
716
730
  }
717
- invalidateProjectsCache(config);
731
+ invalidateProjectsCache(resolvedConfig);
718
732
  invalidateSessionResultCache(projectName);
719
733
 
720
734
  return {
@@ -766,7 +780,8 @@ function saveSessionOrder(projectName, order) {
766
780
 
767
781
  // Delete a project (remove the entire project directory)
768
782
  function deleteProject(config, projectName) {
769
- const projectDir = path.join(config.projectsDir, projectName);
783
+ const resolvedConfig = withResolvedProjectsDir(config);
784
+ const projectDir = path.join(resolvedConfig.projectsDir, projectName);
770
785
 
771
786
  if (!fs.existsSync(projectDir)) {
772
787
  throw new Error('Project not found');
@@ -776,19 +791,20 @@ function deleteProject(config, projectName) {
776
791
  fs.rmSync(projectDir, { recursive: true, force: true });
777
792
 
778
793
  // Remove from order file if exists
779
- const order = getProjectOrder(config);
794
+ const order = getProjectOrder(resolvedConfig);
780
795
  const newOrder = order.filter(name => name !== projectName);
781
796
  if (newOrder.length !== order.length) {
782
- saveProjectOrder(config, newOrder);
797
+ saveProjectOrder(resolvedConfig, newOrder);
783
798
  }
784
799
 
785
- invalidateProjectsCache(config);
800
+ invalidateProjectsCache(resolvedConfig);
786
801
  return { success: true };
787
802
  }
788
803
 
789
804
  // Search sessions for keyword
790
805
  function searchSessions(config, projectName, keyword, contextLength = 15) {
791
- const projectDir = path.join(config.projectsDir, projectName);
806
+ const resolvedConfig = withResolvedProjectsDir(config);
807
+ const projectDir = path.join(resolvedConfig.projectsDir, projectName);
792
808
 
793
809
  if (!fs.existsSync(projectDir)) {
794
810
  return [];