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.
- package/dist/web/assets/{Analytics-BuBlsbcI.js → Analytics-CnA75GxO.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-DQczxuOX.js → ConfigTemplates-D8XEscQY.js} +1 -1
- package/dist/web/assets/{Home-BvAzzWnB.js → Home-BiaKZMah.js} +1 -1
- package/dist/web/assets/{PluginManager-C8gAlaps.js → PluginManager-BzKZ8TYu.js} +1 -1
- package/dist/web/assets/{ProjectList-nqgi6_cR.js → ProjectList-BPfG_92X.js} +1 -1
- package/dist/web/assets/{SessionList-D8AxMYy6.js → SessionList-BIVtw00v.js} +1 -1
- package/dist/web/assets/{SkillManager-BDbNbNkK.js → SkillManager-fxOvFTYD.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-DGGaGW2c.js → WorkspaceManager-Ch2mM8jm.js} +1 -1
- package/dist/web/assets/{index-y-eEayxR.js → index-BpBFF3b2.js} +2 -2
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/src/commands/port-config.js +0 -2
- package/src/commands/switch.js +1 -5
- package/src/commands/workspace.js +35 -3
- package/src/config/default.js +0 -6
- package/src/config/loader.js +76 -5
- package/src/server/api/config.js +0 -3
- package/src/server/api/settings.js +0 -2
- package/src/server/api/workspaces.js +39 -1
- package/src/server/services/sessions.js +35 -19
- package/src/server/services/workspace-service.js +220 -98
- package/src/utils/session.js +10 -4
package/dist/web/index.html
CHANGED
|
@@ -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-
|
|
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
|
@@ -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
|
|
package/src/commands/switch.js
CHANGED
|
@@ -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
|
-
|
|
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('');
|
package/src/config/default.js
CHANGED
|
@@ -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,
|
package/src/config/loader.js
CHANGED
|
@@ -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 =
|
|
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:
|
|
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
|
-
|
|
141
|
-
|
|
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
|
};
|
package/src/server/api/config.js
CHANGED
|
@@ -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
|
|
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}${
|
|
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(
|
|
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(
|
|
415
|
+
const data = await buildProjectsWithStats(resolvedConfig);
|
|
404
416
|
if (!Array.isArray(data)) {
|
|
405
|
-
console.warn(`[getProjectsWithStats] Unexpected non-array result for ${
|
|
417
|
+
console.warn(`[getProjectsWithStats] Unexpected non-array result for ${resolvedConfig.projectsDir}, returning empty array.`);
|
|
406
418
|
return [];
|
|
407
419
|
}
|
|
408
|
-
setCachedProjects(
|
|
409
|
-
globalCache.set(`${CacheKeys.PROJECTS}${
|
|
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 ${
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
794
|
+
const order = getProjectOrder(resolvedConfig);
|
|
780
795
|
const newOrder = order.filter(name => name !== projectName);
|
|
781
796
|
if (newOrder.length !== order.length) {
|
|
782
|
-
saveProjectOrder(
|
|
797
|
+
saveProjectOrder(resolvedConfig, newOrder);
|
|
783
798
|
}
|
|
784
799
|
|
|
785
|
-
invalidateProjectsCache(
|
|
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
|
|
806
|
+
const resolvedConfig = withResolvedProjectsDir(config);
|
|
807
|
+
const projectDir = path.join(resolvedConfig.projectsDir, projectName);
|
|
792
808
|
|
|
793
809
|
if (!fs.existsSync(projectDir)) {
|
|
794
810
|
return [];
|