coding-tool-x 3.7.6 → 3.7.7

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 (39) hide show
  1. package/dist/web/assets/{Analytics-wCRLv26m.js → Analytics-B6FzPvdJ.js} +1 -1
  2. package/dist/web/assets/{ConfigTemplates-aa1blEHh.js → ConfigTemplates-DXVnqR5c.js} +1 -1
  3. package/dist/web/assets/{Home-eIxO50-k.js → Home-FHJB7hYI.js} +1 -1
  4. package/dist/web/assets/{PluginManager-BAwGFNjN.js → PluginManager-D8RToURX.js} +1 -1
  5. package/dist/web/assets/ProjectList-Cdz4jnOg.js +1 -0
  6. package/dist/web/assets/ProjectList-VeMNXfgO.css +1 -0
  7. package/dist/web/assets/SessionList-C4chCPxK.css +1 -0
  8. package/dist/web/assets/SessionList-XYydFlQg.js +1 -0
  9. package/dist/web/assets/{SkillManager-Bhzs1O5S.js → SkillManager-Bu9p7mMb.js} +1 -1
  10. package/dist/web/assets/{WorkspaceManager-CLdX-pTT.js → WorkspaceManager-Bx0Gj3Mk.js} +1 -1
  11. package/dist/web/assets/index-DxkAvn4g.js +2 -0
  12. package/dist/web/assets/index-S8wrU2Nf.css +1 -0
  13. package/dist/web/index.html +2 -2
  14. package/package.json +1 -1
  15. package/src/commands/daemon.js +3 -1
  16. package/src/commands/port-config.js +0 -2
  17. package/src/commands/switch.js +1 -5
  18. package/src/commands/ui.js +4 -3
  19. package/src/commands/workspace.js +35 -3
  20. package/src/config/loader.js +76 -5
  21. package/src/index.js +11 -0
  22. package/src/server/api/config.js +0 -3
  23. package/src/server/api/settings.js +0 -2
  24. package/src/server/api/workspaces.js +39 -1
  25. package/src/server/services/dashboard-snapshot-worker.js +1 -0
  26. package/src/server/services/https-cert.js +3 -2
  27. package/src/server/services/omp-auth-providers.js +1 -0
  28. package/src/server/services/session-snapshots.js +1 -0
  29. package/src/server/services/sessions.js +35 -19
  30. package/src/server/services/web-build.js +2 -1
  31. package/src/server/services/workspace-service.js +204 -98
  32. package/src/utils/cli-flags.js +7 -0
  33. package/src/utils/session.js +10 -4
  34. package/dist/web/assets/ProjectList-CRiAEixW.js +0 -1
  35. package/dist/web/assets/ProjectList-Dw2omgOH.css +0 -1
  36. package/dist/web/assets/SessionList-BQ0FW6Gd.js +0 -1
  37. package/dist/web/assets/SessionList-kYJ6-Ies.css +0 -1
  38. package/dist/web/assets/index-B_nFqixU.css +0 -1
  39. package/dist/web/assets/index-ClMNA9Hf.js +0 -2
@@ -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
  };
package/src/index.js CHANGED
@@ -17,6 +17,7 @@ const { ensureStorageDirMigrated } = require('./config/paths');
17
17
  const { normalizePlatformKey } = require('./shared/platforms');
18
18
  const PluginManager = require('./plugins/plugin-manager');
19
19
  const eventBus = require('./plugins/event-bus');
20
+ const { hasHostFlag } = require('./utils/cli-flags');
20
21
  const chalk = require('chalk');
21
22
  const path = require('path');
22
23
  const fs = require('fs');
@@ -54,9 +55,12 @@ function showHelp() {
54
55
  console.log(' ctx ui 前台启动 Web UI(仅本地访问)');
55
56
  console.log(' ctx ui --https 前台启动 Web UI(本地 HTTPS)');
56
57
  console.log(' ctx ui --host 前台启动 Web UI(允许 LAN 访问,默认允许远程写操作)');
58
+ console.log(' ctx ui --hosts 前台启动 Web UI(同 --host)');
59
+ console.log(' ctx --hosts 前台启动 Web UI(同 ctx ui --host)');
57
60
  console.log(' ctx ui start 后台启动 Web UI');
58
61
  console.log(' ctx ui start --https 后台启动 Web UI(本地 HTTPS)');
59
62
  console.log(' ctx ui start --host 后台启动 Web UI(允许 LAN 访问,默认允许远程写操作)');
63
+ console.log(' ctx ui start --hosts 后台启动 Web UI(同 --host)');
60
64
  console.log(' ctx ui stop 停止 Web UI');
61
65
  console.log(' ctx ui restart 重启 Web UI\n');
62
66
  console.log(chalk.gray(' 提示: 如需禁止 LAN 远程写操作,可设置 CC_TOOL_ALLOW_REMOTE_WRITE=false\n'));
@@ -149,6 +153,13 @@ async function main() {
149
153
  // 处理命令行参数
150
154
  const args = process.argv.slice(2);
151
155
 
156
+ // LAN 快捷入口,等同于 ctx ui --host/--hosts
157
+ if (hasHostFlag(process.argv) && !args.some((arg) => arg && !arg.startsWith('-'))) {
158
+ const { handleUI } = require('./commands/ui');
159
+ await handleUI();
160
+ return;
161
+ }
162
+
152
163
  // --version 或 -v - 显示版本号
153
164
  if (args[0] === '--version' || args[0] === '-v') {
154
165
  console.log(getVersion());
@@ -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
 
@@ -490,7 +488,6 @@ router.post('/advanced', (req, res) => {
490
488
  // 更新配置
491
489
  const newConfig = {
492
490
  ...config,
493
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
494
491
  ports: normalizedPorts,
495
492
  maxLogs: maxLogs !== undefined ? parseInt(maxLogs) : config.maxLogs,
496
493
  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
  });
@@ -130,6 +130,7 @@ function runDashboardSnapshotWorker(kind, source, config = {}, options = {}) {
130
130
  return new Promise((resolve, reject) => {
131
131
  const child = fork(__filename, [], {
132
132
  stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
133
+ windowsHide: true,
133
134
  env: {
134
135
  ...process.env,
135
136
  CC_TOOL_DASHBOARD_SNAPSHOT_WORKER: '1'
@@ -42,7 +42,7 @@ function resolveOpenSslCommand() {
42
42
 
43
43
  for (const command of commands) {
44
44
  try {
45
- execFileSync(command, ['version'], { stdio: 'ignore' });
45
+ execFileSync(command, ['version'], { stdio: 'ignore', windowsHide: true });
46
46
  return command;
47
47
  } catch {
48
48
  // Try next candidate.
@@ -96,7 +96,8 @@ function generatePemCertificateWithOpenSsl(command) {
96
96
  '-config',
97
97
  configPath
98
98
  ], {
99
- stdio: 'ignore'
99
+ stdio: 'ignore',
100
+ windowsHide: true
100
101
  });
101
102
 
102
103
  writeMeta({
@@ -351,6 +351,7 @@ function warmOmpAuthProviderSnapshot(options = {}) {
351
351
  try {
352
352
  const child = fork(workerPath, [], {
353
353
  stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
354
+ windowsHide: true,
354
355
  env: {
355
356
  ...process.env,
356
357
  CC_TOOL_OMP_AUTH_PROVIDER_WORKER: '1'
@@ -46,6 +46,7 @@ function runSessionSnapshotWorker(source, projectName, config = {}, options = {}
46
46
  const workerPath = path.join(__dirname, 'session-snapshot-worker.js');
47
47
  const child = fork(workerPath, [], {
48
48
  stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
49
+ windowsHide: true,
49
50
  env: {
50
51
  ...process.env,
51
52
  CC_TOOL_SESSION_SNAPSHOT_WORKER: '1'
@@ -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
  }
@@ -672,7 +684,8 @@ async function getSessionsForProject(config, projectName, options = {}) {
672
684
 
673
685
  // Delete a session
674
686
  function deleteSession(config, projectName, sessionId) {
675
- const projectDir = path.join(config.projectsDir, projectName);
687
+ const resolvedConfig = withResolvedProjectsDir(config);
688
+ const projectDir = path.join(resolvedConfig.projectsDir, projectName);
676
689
  const sessionFile = path.join(projectDir, sessionId + '.jsonl');
677
690
 
678
691
  if (!fs.existsSync(sessionFile)) {
@@ -680,14 +693,15 @@ function deleteSession(config, projectName, sessionId) {
680
693
  }
681
694
 
682
695
  fs.unlinkSync(sessionFile);
683
- invalidateProjectsCache(config);
696
+ invalidateProjectsCache(resolvedConfig);
684
697
  invalidateSessionResultCache(projectName);
685
698
  return { success: true };
686
699
  }
687
700
 
688
701
  // Fork a session
689
702
  function forkSession(config, projectName, sessionId, options = {}) {
690
- const projectDir = path.join(config.projectsDir, projectName);
703
+ const resolvedConfig = withResolvedProjectsDir(config);
704
+ const projectDir = path.join(resolvedConfig.projectsDir, projectName);
691
705
  const sessionFile = path.join(projectDir, sessionId + '.jsonl');
692
706
 
693
707
  if (!fs.existsSync(sessionFile)) {
@@ -716,7 +730,7 @@ function forkSession(config, projectName, sessionId, options = {}) {
716
730
  const { setAlias } = require('./alias');
717
731
  setAlias(newSessionId, options.alias);
718
732
  }
719
- invalidateProjectsCache(config);
733
+ invalidateProjectsCache(resolvedConfig);
720
734
  invalidateSessionResultCache(projectName);
721
735
 
722
736
  return {
@@ -768,7 +782,8 @@ function saveSessionOrder(projectName, order) {
768
782
 
769
783
  // Delete a project (remove the entire project directory)
770
784
  function deleteProject(config, projectName) {
771
- const projectDir = path.join(config.projectsDir, projectName);
785
+ const resolvedConfig = withResolvedProjectsDir(config);
786
+ const projectDir = path.join(resolvedConfig.projectsDir, projectName);
772
787
 
773
788
  if (!fs.existsSync(projectDir)) {
774
789
  throw new Error('Project not found');
@@ -778,19 +793,20 @@ function deleteProject(config, projectName) {
778
793
  fs.rmSync(projectDir, { recursive: true, force: true });
779
794
 
780
795
  // Remove from order file if exists
781
- const order = getProjectOrder(config);
796
+ const order = getProjectOrder(resolvedConfig);
782
797
  const newOrder = order.filter(name => name !== projectName);
783
798
  if (newOrder.length !== order.length) {
784
- saveProjectOrder(config, newOrder);
799
+ saveProjectOrder(resolvedConfig, newOrder);
785
800
  }
786
801
 
787
- invalidateProjectsCache(config);
802
+ invalidateProjectsCache(resolvedConfig);
788
803
  return { success: true };
789
804
  }
790
805
 
791
806
  // Search sessions for keyword
792
807
  function searchSessions(config, projectName, keyword, contextLength = 15) {
793
- const projectDir = path.join(config.projectsDir, projectName);
808
+ const resolvedConfig = withResolvedProjectsDir(config);
809
+ const projectDir = path.join(resolvedConfig.projectsDir, projectName);
794
810
 
795
811
  if (!fs.existsSync(projectDir)) {
796
812
  return [];
@@ -101,7 +101,8 @@ function runWebBuild(options = {}) {
101
101
  const child = spawn(npmCommand, ['run', 'build'], {
102
102
  cwd: sourceRoot,
103
103
  env,
104
- stdio
104
+ stdio,
105
+ windowsHide: true
105
106
  });
106
107
 
107
108
  child.once('error', (error) => {