@siteboon/claude-code-ui 1.16.4 → 1.17.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.
@@ -1,5 +1,5 @@
1
1
  import express from 'express';
2
- import { exec } from 'child_process';
2
+ import { exec, spawn } from 'child_process';
3
3
  import { promisify } from 'util';
4
4
  import path from 'path';
5
5
  import { promises as fs } from 'fs';
@@ -10,6 +10,43 @@ import { spawnCursor } from '../cursor-cli.js';
10
10
  const router = express.Router();
11
11
  const execAsync = promisify(exec);
12
12
 
13
+ function spawnAsync(command, args, options = {}) {
14
+ return new Promise((resolve, reject) => {
15
+ const child = spawn(command, args, {
16
+ ...options,
17
+ shell: false,
18
+ });
19
+
20
+ let stdout = '';
21
+ let stderr = '';
22
+
23
+ child.stdout.on('data', (data) => {
24
+ stdout += data.toString();
25
+ });
26
+
27
+ child.stderr.on('data', (data) => {
28
+ stderr += data.toString();
29
+ });
30
+
31
+ child.on('error', (error) => {
32
+ reject(error);
33
+ });
34
+
35
+ child.on('close', (code) => {
36
+ if (code === 0) {
37
+ resolve({ stdout, stderr });
38
+ return;
39
+ }
40
+
41
+ const error = new Error(`Command failed: ${command} ${args.join(' ')}`);
42
+ error.code = code;
43
+ error.stdout = stdout;
44
+ error.stderr = stderr;
45
+ reject(error);
46
+ });
47
+ });
48
+ }
49
+
13
50
  // Helper function to get the actual project path from the encoded project name
14
51
  async function getActualProjectPath(projectName) {
15
52
  try {
@@ -60,19 +97,16 @@ async function validateGitRepository(projectPath) {
60
97
  }
61
98
 
62
99
  try {
63
- // Use --show-toplevel to get the root of the git repository
64
- const { stdout: gitRoot } = await execAsync('git rev-parse --show-toplevel', { cwd: projectPath });
65
- const normalizedGitRoot = path.resolve(gitRoot.trim());
66
- const normalizedProjectPath = path.resolve(projectPath);
67
-
68
- // Ensure the git root matches our project path (prevent using parent git repos)
69
- if (normalizedGitRoot !== normalizedProjectPath) {
70
- throw new Error(`Project directory is not a git repository. This directory is inside a git repository at ${normalizedGitRoot}, but git operations should be run from the repository root.`);
71
- }
72
- } catch (error) {
73
- if (error.message.includes('Project directory is not a git repository')) {
74
- throw error;
100
+ // Allow any directory that is inside a work tree (repo root or nested folder).
101
+ const { stdout: insideWorkTreeOutput } = await execAsync('git rev-parse --is-inside-work-tree', { cwd: projectPath });
102
+ const isInsideWorkTree = insideWorkTreeOutput.trim() === 'true';
103
+ if (!isInsideWorkTree) {
104
+ throw new Error('Not inside a git work tree');
75
105
  }
106
+
107
+ // Ensure git can resolve the repository root for this directory.
108
+ await execAsync('git rev-parse --show-toplevel', { cwd: projectPath });
109
+ } catch {
76
110
  throw new Error('Not a git repository. This directory does not contain a .git folder. Initialize a git repository with "git init" to use source control features.');
77
111
  }
78
112
  }
@@ -445,11 +479,17 @@ router.get('/commits', async (req, res) => {
445
479
 
446
480
  try {
447
481
  const projectPath = await getActualProjectPath(project);
482
+ await validateGitRepository(projectPath);
483
+ const parsedLimit = Number.parseInt(String(limit), 10);
484
+ const safeLimit = Number.isFinite(parsedLimit) && parsedLimit > 0
485
+ ? Math.min(parsedLimit, 100)
486
+ : 10;
448
487
 
449
488
  // Get commit log with stats
450
- const { stdout } = await execAsync(
451
- `git log --pretty=format:'%H|%an|%ae|%ad|%s' --date=relative -n ${limit}`,
452
- { cwd: projectPath }
489
+ const { stdout } = await spawnAsync(
490
+ 'git',
491
+ ['log', '--pretty=format:%H|%an|%ae|%ad|%s', '--date=relative', '-n', String(safeLimit)],
492
+ { cwd: projectPath },
453
493
  );
454
494
 
455
495
  const commits = stdout
@@ -1125,4 +1165,4 @@ router.post('/delete-untracked', async (req, res) => {
1125
1165
  }
1126
1166
  });
1127
1167
 
1128
- export default router;
1168
+ export default router;