@openchamber/web 1.3.1 → 1.3.3

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/index.html CHANGED
@@ -160,10 +160,10 @@
160
160
  pointer-events: none;
161
161
  }
162
162
  </style>
163
- <script type="module" crossorigin src="/assets/index-BleCEZso.js"></script>
163
+ <script type="module" crossorigin src="/assets/index-BAp9MHgd.js"></script>
164
164
  <link rel="modulepreload" crossorigin href="/assets/vendor-.pnpm-CFPpXnpS.js">
165
165
  <link rel="stylesheet" crossorigin href="/assets/vendor--B3aGWKBE.css">
166
- <link rel="stylesheet" crossorigin href="/assets/index-a7wSdHza.css">
166
+ <link rel="stylesheet" crossorigin href="/assets/index-DST7Nr3C.css">
167
167
  </head>
168
168
  <body class="h-full bg-background text-foreground">
169
169
  <div id="root" class="h-full">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openchamber/web",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./server/index.js",
@@ -25,7 +25,7 @@
25
25
  "@fontsource/ibm-plex-mono": "^5.2.7",
26
26
  "@fontsource/ibm-plex-sans": "^5.1.1",
27
27
  "@ibm/plex": "^6.4.1",
28
- "@opencode-ai/sdk": "^1.0.65",
28
+ "@opencode-ai/sdk": "^1.0.185",
29
29
  "@radix-ui/react-collapsible": "^1.1.12",
30
30
  "@radix-ui/react-dialog": "^1.1.15",
31
31
  "@radix-ui/react-dropdown-menu": "^2.1.16",
package/server/index.js CHANGED
@@ -37,6 +37,27 @@ const FILE_SEARCH_EXCLUDED_DIRS = new Set([
37
37
  'logs'
38
38
  ]);
39
39
 
40
+ const normalizeDirectoryPath = (value) => {
41
+ if (typeof value !== 'string') {
42
+ return value;
43
+ }
44
+
45
+ const trimmed = value.trim();
46
+ if (!trimmed) {
47
+ return trimmed;
48
+ }
49
+
50
+ if (trimmed === '~') {
51
+ return os.homedir();
52
+ }
53
+
54
+ if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) {
55
+ return path.join(os.homedir(), trimmed.slice(2));
56
+ }
57
+
58
+ return trimmed;
59
+ };
60
+
40
61
  const normalizeRelativeSearchPath = (rootPath, targetPath) => {
41
62
  const relative = path.relative(rootPath, targetPath) || path.basename(targetPath);
42
63
  return relative.split(path.sep).join('/') || targetPath;
@@ -677,6 +698,89 @@ function buildOpenCodeUrl(path, prefixOverride) {
677
698
  return `http://localhost:${openCodePort}${fullPath}`;
678
699
  }
679
700
 
701
+ function parseSseDataPayload(block) {
702
+ if (!block || typeof block !== 'string') {
703
+ return null;
704
+ }
705
+ const dataLines = block
706
+ .split('\n')
707
+ .filter((line) => line.startsWith('data:'))
708
+ .map((line) => line.slice(5).replace(/^\s/, ''));
709
+
710
+ if (dataLines.length === 0) {
711
+ return null;
712
+ }
713
+
714
+ const payloadText = dataLines.join('\n').trim();
715
+ if (!payloadText) {
716
+ return null;
717
+ }
718
+
719
+ try {
720
+ return JSON.parse(payloadText);
721
+ } catch {
722
+ return null;
723
+ }
724
+ }
725
+
726
+ function deriveSessionActivity(payload) {
727
+ if (!payload || typeof payload !== 'object') {
728
+ return null;
729
+ }
730
+
731
+ if (payload.type === 'session.status') {
732
+ const status = payload.properties?.status;
733
+ const sessionId = payload.properties?.sessionID;
734
+ const statusType = status?.type;
735
+
736
+ if (typeof sessionId === 'string' && sessionId.length > 0 && typeof statusType === 'string') {
737
+ const phase = statusType === 'busy' || statusType === 'retry' ? 'busy' : 'idle';
738
+ return { sessionId, phase };
739
+ }
740
+ }
741
+
742
+ if (payload.type === 'message.updated') {
743
+ const info = payload.properties?.info;
744
+ const sessionId = info?.sessionID;
745
+ const role = info?.role;
746
+ const finish = info?.finish;
747
+ if (typeof sessionId === 'string' && sessionId.length > 0 && role === 'assistant' && finish === 'stop') {
748
+ return { sessionId, phase: 'cooldown' };
749
+ }
750
+ }
751
+
752
+ if (payload.type === 'message.part.updated') {
753
+ const info = payload.properties?.info;
754
+ const part = payload.properties?.part;
755
+ const sessionId = info?.sessionID ?? part?.sessionID ?? payload.properties?.sessionID;
756
+ const role = info?.role;
757
+ const partType = part?.type;
758
+ const reason = part?.reason;
759
+ if (
760
+ typeof sessionId === 'string' &&
761
+ sessionId.length > 0 &&
762
+ role === 'assistant' &&
763
+ partType === 'step-finish' &&
764
+ reason === 'stop'
765
+ ) {
766
+ return { sessionId, phase: 'cooldown' };
767
+ }
768
+ }
769
+
770
+ if (payload.type === 'session.idle') {
771
+ const sessionId = payload.properties?.sessionID;
772
+ if (typeof sessionId === 'string' && sessionId.length > 0) {
773
+ return { sessionId, phase: 'idle' };
774
+ }
775
+ }
776
+
777
+ return null;
778
+ }
779
+
780
+ function writeSseEvent(res, payload) {
781
+ res.write(`data: ${JSON.stringify(payload)}\n\n`);
782
+ }
783
+
680
784
  function extractApiPrefixFromUrl(urlString, expectedSuffix) {
681
785
  if (!urlString) {
682
786
  return null;
@@ -1764,6 +1868,129 @@ async function main(options = {}) {
1764
1868
  }
1765
1869
  });
1766
1870
 
1871
+ app.get('/api/event', async (req, res) => {
1872
+ if (!openCodePort) {
1873
+ return res.status(503).json({ error: 'OpenCode service unavailable' });
1874
+ }
1875
+
1876
+ if (!openCodeApiPrefixDetected) {
1877
+ try {
1878
+ await detectOpenCodeApiPrefix();
1879
+ } catch {
1880
+ // ignore detection failures
1881
+ }
1882
+ }
1883
+
1884
+ let targetUrl;
1885
+ try {
1886
+ const prefix = openCodeApiPrefixDetected ? openCodeApiPrefix : '';
1887
+ targetUrl = new URL(buildOpenCodeUrl('/event', prefix));
1888
+ } catch (error) {
1889
+ return res.status(503).json({ error: 'OpenCode service unavailable' });
1890
+ }
1891
+
1892
+ const directoryParam = Array.isArray(req.query.directory)
1893
+ ? req.query.directory[0]
1894
+ : req.query.directory;
1895
+ if (typeof directoryParam === 'string' && directoryParam.trim().length > 0) {
1896
+ targetUrl.searchParams.set('directory', directoryParam.trim());
1897
+ }
1898
+
1899
+ const headers = {
1900
+ Accept: 'text/event-stream',
1901
+ 'Cache-Control': 'no-cache',
1902
+ Connection: 'keep-alive'
1903
+ };
1904
+
1905
+ const lastEventId = req.header('Last-Event-ID');
1906
+ if (typeof lastEventId === 'string' && lastEventId.length > 0) {
1907
+ headers['Last-Event-ID'] = lastEventId;
1908
+ }
1909
+
1910
+ const controller = new AbortController();
1911
+ const cleanup = () => {
1912
+ if (!controller.signal.aborted) {
1913
+ controller.abort();
1914
+ }
1915
+ };
1916
+
1917
+ req.on('close', cleanup);
1918
+ req.on('error', cleanup);
1919
+
1920
+ let upstream;
1921
+ try {
1922
+ upstream = await fetch(targetUrl.toString(), {
1923
+ headers,
1924
+ signal: controller.signal,
1925
+ });
1926
+ } catch (error) {
1927
+ return res.status(502).json({ error: 'Failed to connect to OpenCode event stream' });
1928
+ }
1929
+
1930
+ if (!upstream.ok || !upstream.body) {
1931
+ return res.status(502).json({ error: `OpenCode event stream unavailable (${upstream.status})` });
1932
+ }
1933
+
1934
+ res.setHeader('Content-Type', 'text/event-stream');
1935
+ res.setHeader('Cache-Control', 'no-cache');
1936
+ res.setHeader('Connection', 'keep-alive');
1937
+ res.setHeader('X-Accel-Buffering', 'no');
1938
+
1939
+ if (typeof res.flushHeaders === 'function') {
1940
+ res.flushHeaders();
1941
+ }
1942
+
1943
+ const decoder = new TextDecoder();
1944
+ const reader = upstream.body.getReader();
1945
+ let buffer = '';
1946
+
1947
+ const forwardBlock = (block) => {
1948
+ if (!block) return;
1949
+ res.write(`${block}\n\n`);
1950
+ const payload = parseSseDataPayload(block);
1951
+ const activity = deriveSessionActivity(payload);
1952
+ if (activity) {
1953
+ writeSseEvent(res, {
1954
+ type: 'openchamber:session-activity',
1955
+ properties: {
1956
+ sessionId: activity.sessionId,
1957
+ phase: activity.phase,
1958
+ }
1959
+ });
1960
+ }
1961
+ };
1962
+
1963
+ try {
1964
+ while (true) {
1965
+ const { value, done } = await reader.read();
1966
+ if (done) break;
1967
+ buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, '\n');
1968
+
1969
+ let separatorIndex;
1970
+ while ((separatorIndex = buffer.indexOf('\n\n')) !== -1) {
1971
+ const block = buffer.slice(0, separatorIndex);
1972
+ buffer = buffer.slice(separatorIndex + 2);
1973
+ forwardBlock(block);
1974
+ }
1975
+ }
1976
+
1977
+ if (buffer.trim().length > 0) {
1978
+ forwardBlock(buffer.trim());
1979
+ }
1980
+ } catch (error) {
1981
+ if (!controller.signal.aborted) {
1982
+ console.warn('SSE proxy stream error:', error);
1983
+ }
1984
+ } finally {
1985
+ cleanup();
1986
+ try {
1987
+ res.end();
1988
+ } catch {
1989
+ // ignore
1990
+ }
1991
+ }
1992
+ });
1993
+
1767
1994
  app.get('/api/config/settings', async (_req, res) => {
1768
1995
  try {
1769
1996
  const settings = await readSettingsFromDisk();
@@ -2497,8 +2724,11 @@ async function main(options = {}) {
2497
2724
  const worktrees = await getWorktrees(directory);
2498
2725
  res.json(worktrees);
2499
2726
  } catch (error) {
2500
- console.error('Failed to get worktrees:', error);
2501
- res.status(500).json({ error: error.message || 'Failed to get worktrees' });
2727
+ // Worktrees are an optional feature. Avoid repeated 500s (and repeated client retries)
2728
+ // when the directory isn't a git repo or uses shell shorthand like "~/".
2729
+ console.warn('Failed to get worktrees, returning empty list:', error?.message || error);
2730
+ res.setHeader('X-OpenChamber-Warning', 'git worktrees unavailable');
2731
+ res.json([]);
2502
2732
  }
2503
2733
  });
2504
2734
 
@@ -2637,15 +2867,16 @@ async function main(options = {}) {
2637
2867
  return res.status(400).json({ error: 'Path is required' });
2638
2868
  }
2639
2869
 
2640
- const normalizedPath = path.normalize(dirPath);
2870
+ const expandedPath = normalizeDirectoryPath(dirPath);
2871
+ const normalizedPath = path.normalize(expandedPath);
2641
2872
  if (normalizedPath.includes('..')) {
2642
2873
  return res.status(400).json({ error: 'Invalid path: path traversal not allowed' });
2643
2874
  }
2644
2875
 
2645
- fs.mkdirSync(dirPath, { recursive: true });
2646
- console.log(`Created directory: ${dirPath}`);
2876
+ const resolvedPath = path.resolve(expandedPath);
2877
+ fs.mkdirSync(resolvedPath, { recursive: true });
2647
2878
 
2648
- res.json({ success: true, path: dirPath });
2879
+ res.json({ success: true, path: resolvedPath });
2649
2880
  } catch (error) {
2650
2881
  console.error('Failed to create directory:', error);
2651
2882
  res.status(500).json({ error: error.message || 'Failed to create directory' });
@@ -2659,7 +2890,7 @@ async function main(options = {}) {
2659
2890
  return res.status(400).json({ error: 'Path is required' });
2660
2891
  }
2661
2892
 
2662
- const resolvedPath = path.resolve(requestedPath);
2893
+ const resolvedPath = path.resolve(normalizeDirectoryPath(requestedPath));
2663
2894
  let stats;
2664
2895
  try {
2665
2896
  stats = await fsPromises.stat(resolvedPath);
@@ -2705,7 +2936,7 @@ async function main(options = {}) {
2705
2936
  : os.homedir();
2706
2937
 
2707
2938
  try {
2708
- const resolvedPath = path.resolve(rawPath);
2939
+ const resolvedPath = path.resolve(normalizeDirectoryPath(rawPath));
2709
2940
 
2710
2941
  const stats = await fsPromises.stat(resolvedPath);
2711
2942
  if (!stats.isDirectory()) {
@@ -2770,7 +3001,7 @@ async function main(options = {}) {
2770
3001
  const limit = Math.max(1, Math.min(parsedLimit, MAX_FILE_SEARCH_LIMIT));
2771
3002
 
2772
3003
  try {
2773
- const resolvedRoot = path.resolve(rawRoot);
3004
+ const resolvedRoot = path.resolve(normalizeDirectoryPath(rawRoot));
2774
3005
  const stats = await fsPromises.stat(resolvedRoot);
2775
3006
  if (!stats.isDirectory()) {
2776
3007
  return res.status(400).json({ error: 'Specified root is not a directory' });
@@ -1,27 +1,51 @@
1
1
  import simpleGit from 'simple-git';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
+ import os from 'os';
4
5
  import { execFile } from 'child_process';
5
6
  import { promisify } from 'util';
6
7
 
7
8
  const fsp = fs.promises;
8
9
  const execFileAsync = promisify(execFile);
9
10
 
11
+ const normalizeDirectoryPath = (value) => {
12
+ if (typeof value !== 'string') {
13
+ return value;
14
+ }
15
+
16
+ const trimmed = value.trim();
17
+ if (!trimmed) {
18
+ return trimmed;
19
+ }
20
+
21
+ if (trimmed === '~') {
22
+ return os.homedir();
23
+ }
24
+
25
+ if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) {
26
+ return path.join(os.homedir(), trimmed.slice(2));
27
+ }
28
+
29
+ return trimmed;
30
+ };
31
+
10
32
  export async function isGitRepository(directory) {
11
- if (!directory || !fs.existsSync(directory)) {
33
+ const directoryPath = normalizeDirectoryPath(directory);
34
+ if (!directoryPath || !fs.existsSync(directoryPath)) {
12
35
  return false;
13
36
  }
14
37
 
15
- const gitDir = path.join(directory, '.git');
38
+ const gitDir = path.join(directoryPath, '.git');
16
39
  return fs.existsSync(gitDir);
17
40
  }
18
41
 
19
42
  export async function ensureOpenChamberIgnored(directory) {
20
- if (!directory || !fs.existsSync(directory)) {
43
+ const directoryPath = normalizeDirectoryPath(directory);
44
+ if (!directoryPath || !fs.existsSync(directoryPath)) {
21
45
  return false;
22
46
  }
23
47
 
24
- const gitDir = path.join(directory, '.git');
48
+ const gitDir = path.join(directoryPath, '.git');
25
49
  if (!fs.existsSync(gitDir)) {
26
50
  return false;
27
51
  }
@@ -78,7 +102,7 @@ export async function getGlobalIdentity() {
78
102
  }
79
103
 
80
104
  export async function getCurrentIdentity(directory) {
81
- const git = simpleGit(directory);
105
+ const git = simpleGit(normalizeDirectoryPath(directory));
82
106
 
83
107
  try {
84
108
 
@@ -110,7 +134,7 @@ export async function getCurrentIdentity(directory) {
110
134
  }
111
135
 
112
136
  export async function setLocalIdentity(directory, profile) {
113
- const git = simpleGit(directory);
137
+ const git = simpleGit(normalizeDirectoryPath(directory));
114
138
 
115
139
  try {
116
140
 
@@ -134,7 +158,8 @@ export async function setLocalIdentity(directory, profile) {
134
158
  }
135
159
 
136
160
  export async function getStatus(directory) {
137
- const git = simpleGit(directory);
161
+ const directoryPath = normalizeDirectoryPath(directory);
162
+ const git = simpleGit(directoryPath);
138
163
 
139
164
  try {
140
165
  // Use -uall to show all untracked files individually, not just directories
@@ -194,7 +219,7 @@ export async function getStatus(directory) {
194
219
  return null;
195
220
  }
196
221
 
197
- const absolutePath = path.join(directory, file.path);
222
+ const absolutePath = path.join(directoryPath, file.path);
198
223
 
199
224
  try {
200
225
  const stat = await fsp.stat(absolutePath);
@@ -266,7 +291,7 @@ export async function getStatus(directory) {
266
291
  }
267
292
 
268
293
  export async function getDiff(directory, { path, staged = false, contextLines = 3 } = {}) {
269
- const git = simpleGit(directory);
294
+ const git = simpleGit(normalizeDirectoryPath(directory));
270
295
 
271
296
  try {
272
297
  const args = ['diff', '--no-color'];
@@ -338,7 +363,8 @@ export async function getFileDiff(directory, { path: filePath, staged = false }
338
363
  throw new Error('directory and path are required for getFileDiff');
339
364
  }
340
365
 
341
- const git = simpleGit(directory);
366
+ const directoryPath = normalizeDirectoryPath(directory);
367
+ const git = simpleGit(directoryPath);
342
368
  const isImage = isImageFile(filePath);
343
369
  const mimeType = isImage ? getImageMimeType(filePath) : null;
344
370
 
@@ -348,7 +374,7 @@ export async function getFileDiff(directory, { path: filePath, staged = false }
348
374
  // For images, use git show with raw output and convert to base64
349
375
  try {
350
376
  const { stdout } = await execFileAsync('git', ['show', `HEAD:${filePath}`], {
351
- cwd: directory,
377
+ cwd: directoryPath,
352
378
  encoding: 'buffer',
353
379
  maxBuffer: 50 * 1024 * 1024, // 50MB max
354
380
  });
@@ -365,7 +391,7 @@ export async function getFileDiff(directory, { path: filePath, staged = false }
365
391
  original = '';
366
392
  }
367
393
 
368
- const fullPath = path.join(directory, filePath);
394
+ const fullPath = path.join(directoryPath, filePath);
369
395
  let modified = '';
370
396
  try {
371
397
  const stat = await fsp.stat(fullPath);
@@ -395,8 +421,9 @@ export async function getFileDiff(directory, { path: filePath, staged = false }
395
421
  }
396
422
 
397
423
  export async function revertFile(directory, filePath) {
398
- const git = simpleGit(directory);
399
- const repoRoot = path.resolve(directory);
424
+ const directoryPath = normalizeDirectoryPath(directory);
425
+ const git = simpleGit(directoryPath);
426
+ const repoRoot = path.resolve(directoryPath);
400
427
  const absoluteTarget = path.resolve(repoRoot, filePath);
401
428
 
402
429
  if (!absoluteTarget.startsWith(repoRoot + path.sep) && absoluteTarget !== repoRoot) {
@@ -460,7 +487,7 @@ export async function collectDiffs(directory, files = []) {
460
487
  }
461
488
 
462
489
  export async function pull(directory, options = {}) {
463
- const git = simpleGit(directory);
490
+ const git = simpleGit(normalizeDirectoryPath(directory));
464
491
 
465
492
  try {
466
493
  const result = await git.pull(
@@ -483,7 +510,7 @@ export async function pull(directory, options = {}) {
483
510
  }
484
511
 
485
512
  export async function push(directory, options = {}) {
486
- const git = simpleGit(directory);
513
+ const git = simpleGit(normalizeDirectoryPath(directory));
487
514
 
488
515
  try {
489
516
  const result = await git.push(
@@ -510,7 +537,7 @@ export async function deleteRemoteBranch(directory, options = {}) {
510
537
  throw new Error('branch is required to delete remote branch');
511
538
  }
512
539
 
513
- const git = simpleGit(directory);
540
+ const git = simpleGit(normalizeDirectoryPath(directory));
514
541
  const targetBranch = branch.startsWith('refs/heads/')
515
542
  ? branch.substring('refs/heads/'.length)
516
543
  : branch;
@@ -526,7 +553,7 @@ export async function deleteRemoteBranch(directory, options = {}) {
526
553
  }
527
554
 
528
555
  export async function fetch(directory, options = {}) {
529
- const git = simpleGit(directory);
556
+ const git = simpleGit(normalizeDirectoryPath(directory));
530
557
 
531
558
  try {
532
559
  await git.fetch(
@@ -543,7 +570,7 @@ export async function fetch(directory, options = {}) {
543
570
  }
544
571
 
545
572
  export async function commit(directory, message, options = {}) {
546
- const git = simpleGit(directory);
573
+ const git = simpleGit(normalizeDirectoryPath(directory));
547
574
 
548
575
  try {
549
576
 
@@ -573,7 +600,7 @@ export async function commit(directory, message, options = {}) {
573
600
  }
574
601
 
575
602
  export async function getBranches(directory) {
576
- const git = simpleGit(directory);
603
+ const git = simpleGit(normalizeDirectoryPath(directory));
577
604
 
578
605
  try {
579
606
  const result = await git.branch();
@@ -627,7 +654,7 @@ async function filterActiveRemoteBranches(git, remoteBranches) {
627
654
  }
628
655
 
629
656
  export async function createBranch(directory, branchName, options = {}) {
630
- const git = simpleGit(directory);
657
+ const git = simpleGit(normalizeDirectoryPath(directory));
631
658
 
632
659
  try {
633
660
  await git.checkoutBranch(branchName, options.startPoint || 'HEAD');
@@ -639,7 +666,7 @@ export async function createBranch(directory, branchName, options = {}) {
639
666
  }
640
667
 
641
668
  export async function checkoutBranch(directory, branchName) {
642
- const git = simpleGit(directory);
669
+ const git = simpleGit(normalizeDirectoryPath(directory));
643
670
 
644
671
  try {
645
672
  await git.checkout(branchName);
@@ -651,7 +678,12 @@ export async function checkoutBranch(directory, branchName) {
651
678
  }
652
679
 
653
680
  export async function getWorktrees(directory) {
654
- const git = simpleGit(directory);
681
+ const directoryPath = normalizeDirectoryPath(directory);
682
+ if (!directoryPath || !fs.existsSync(directoryPath) || !fs.existsSync(path.join(directoryPath, '.git'))) {
683
+ return [];
684
+ }
685
+
686
+ const git = simpleGit(directoryPath);
655
687
 
656
688
  try {
657
689
  const result = await git.raw(['worktree', 'list', '--porcelain']);
@@ -684,13 +716,13 @@ export async function getWorktrees(directory) {
684
716
 
685
717
  return worktrees;
686
718
  } catch (error) {
687
- console.error('Failed to list worktrees:', error);
688
- throw error;
719
+ console.warn('Failed to list worktrees, returning empty list:', error?.message || error);
720
+ return [];
689
721
  }
690
722
  }
691
723
 
692
724
  export async function addWorktree(directory, worktreePath, branch, options = {}) {
693
- const git = simpleGit(directory);
725
+ const git = simpleGit(normalizeDirectoryPath(directory));
694
726
 
695
727
  try {
696
728
  const args = ['worktree', 'add'];
@@ -719,7 +751,7 @@ export async function addWorktree(directory, worktreePath, branch, options = {})
719
751
  }
720
752
 
721
753
  export async function removeWorktree(directory, worktreePath, options = {}) {
722
- const git = simpleGit(directory);
754
+ const git = simpleGit(normalizeDirectoryPath(directory));
723
755
 
724
756
  try {
725
757
  const args = ['worktree', 'remove', worktreePath];
@@ -738,7 +770,7 @@ export async function removeWorktree(directory, worktreePath, options = {}) {
738
770
  }
739
771
 
740
772
  export async function deleteBranch(directory, branch, options = {}) {
741
- const git = simpleGit(directory);
773
+ const git = simpleGit(normalizeDirectoryPath(directory));
742
774
 
743
775
  try {
744
776
  const branchName = branch.startsWith('refs/heads/')
@@ -754,7 +786,7 @@ export async function deleteBranch(directory, branch, options = {}) {
754
786
  }
755
787
 
756
788
  export async function getLog(directory, options = {}) {
757
- const git = simpleGit(directory);
789
+ const git = simpleGit(normalizeDirectoryPath(directory));
758
790
 
759
791
  try {
760
792
  const maxCount = options.maxCount || 50;
@@ -852,7 +884,7 @@ export async function getLog(directory, options = {}) {
852
884
  }
853
885
 
854
886
  export async function isLinkedWorktree(directory) {
855
- const git = simpleGit(directory);
887
+ const git = simpleGit(normalizeDirectoryPath(directory));
856
888
  try {
857
889
  const [gitDir, gitCommonDir] = await Promise.all([
858
890
  git.raw(['rev-parse', '--git-dir']).then((output) => output.trim()),
@@ -866,7 +898,7 @@ export async function isLinkedWorktree(directory) {
866
898
  }
867
899
 
868
900
  export async function getCommitFiles(directory, commitHash) {
869
- const git = simpleGit(directory);
901
+ const git = simpleGit(normalizeDirectoryPath(directory));
870
902
 
871
903
  try {
872
904