@nexical/cli 0.11.10 → 0.11.14

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.
@@ -130,4 +130,209 @@ describe('ModuleListCommand', () => {
130
130
  { name: 'mod-a', type: 'backend', version: 'unknown', description: '' },
131
131
  ]);
132
132
  });
133
+ it('should handle missing metadata files', async () => {
134
+ (fs.pathExists as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
135
+ if (p.includes('apps/backend/modules')) return true; // loc.path exists
136
+ return false; // metadata files don't exist
137
+ });
138
+ (fs.readdir as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
139
+ if (p.includes('apps/backend/modules')) return ['mod-no-meta'];
140
+ return [];
141
+ });
142
+ (fs.stat as unknown as { mockResolvedValue: any }).mockResolvedValue({
143
+ isDirectory: () => true,
144
+ });
145
+
146
+ await command.run();
147
+ // eslint-disable-next-line no-console
148
+ expect(console.table).toHaveBeenCalledWith([
149
+ { name: 'mod-no-meta', type: 'backend', version: 'unknown', description: '' },
150
+ ]);
151
+ });
152
+
153
+ it('should support .yml extension for module config', async () => {
154
+ (fs.pathExists as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
155
+ const pStr = p.toString();
156
+ if (
157
+ pStr.endsWith('backend/modules') ||
158
+ pStr.endsWith('frontend/modules') ||
159
+ pStr.endsWith('legacy/modules')
160
+ )
161
+ return true;
162
+ if (pStr.endsWith('yml-mod')) return true;
163
+ if (pStr.endsWith('module.yml')) return true;
164
+ if (pStr.endsWith('module.yaml')) return false;
165
+ if (pStr.endsWith('package.json')) return false;
166
+ return false;
167
+ });
168
+ (fs.readdir as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
169
+ if (p.includes('apps/backend/modules')) return ['yml-mod'];
170
+ return [];
171
+ });
172
+ (fs.stat as unknown as { mockResolvedValue: any }).mockResolvedValue({
173
+ isDirectory: () => true,
174
+ });
175
+ (fs.readFile as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
176
+ if (p.endsWith('module.yml')) return 'name: YmlName\nversion: 1.2.3';
177
+ return '';
178
+ });
179
+
180
+ await command.run();
181
+ // eslint-disable-next-line no-console
182
+ expect(console.table).toHaveBeenCalledWith([
183
+ { name: 'YmlName', type: 'backend', version: '1.2.3', description: '' },
184
+ ]);
185
+ });
186
+ it('should handle both .yaml and .yml existing (favoring .yaml)', async () => {
187
+ (fs.pathExists as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
188
+ const pStr = p.toString();
189
+ if (
190
+ pStr.endsWith('backend/modules') ||
191
+ pStr.endsWith('frontend/modules') ||
192
+ pStr.endsWith('legacy/modules')
193
+ )
194
+ return true;
195
+ if (pStr.endsWith('dual-mod')) return true;
196
+ if (pStr.endsWith('module.yaml')) return true;
197
+ if (pStr.endsWith('module.yml')) return true;
198
+ return false;
199
+ });
200
+ (fs.readdir as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
201
+ if (p.includes('apps/backend/modules')) return ['dual-mod'];
202
+ return [];
203
+ });
204
+ (fs.stat as unknown as { mockResolvedValue: any }).mockResolvedValue({
205
+ isDirectory: () => true,
206
+ });
207
+ (fs.readFile as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
208
+ if (p.endsWith('module.yaml')) return 'name: YamlName';
209
+ if (p.endsWith('module.yml')) return 'name: YmlName';
210
+ return '';
211
+ });
212
+
213
+ await command.run();
214
+ // eslint-disable-next-line no-console
215
+ expect(console.table).toHaveBeenCalledWith(
216
+ expect.arrayContaining([expect.objectContaining({ name: 'YamlName' })]),
217
+ );
218
+ });
219
+ it('should handle only .yaml existing', async () => {
220
+ (fs.pathExists as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
221
+ const pStr = p.toString();
222
+ if (pStr.endsWith('backend/modules')) return true;
223
+ if (pStr.endsWith('yaml-only')) return true;
224
+ if (pStr.endsWith('module.yaml')) return true;
225
+ if (pStr.endsWith('module.yml')) return false;
226
+ return false;
227
+ });
228
+ (fs.readdir as unknown as { mockImplementation: any }).mockImplementation((p: string) => {
229
+ if (p.includes('apps/backend/modules')) return ['yaml-only'];
230
+ return [];
231
+ });
232
+ (fs.stat as unknown as { mockResolvedValue: any }).mockResolvedValue({
233
+ isDirectory: () => true,
234
+ });
235
+ (fs.readFile as any).mockResolvedValue('name: YamlOnly');
236
+ await command.run();
237
+ expect(console.table).toHaveBeenCalledWith(
238
+ expect.arrayContaining([expect.objectContaining({ name: 'YamlOnly' })]),
239
+ );
240
+ });
241
+
242
+ it('should handle both .yaml and .yml missing', async () => {
243
+ (fs.pathExists as any).mockResolvedValue(false);
244
+ (fs.readdir as any).mockImplementation((p: string) => {
245
+ if (p.includes('modules')) return ['no-config'];
246
+ return [];
247
+ });
248
+ (fs.stat as any).mockResolvedValue({ isDirectory: () => true });
249
+ await command.run();
250
+ // Should skip it and NOT call console.table since length 0
251
+ expect(console.table).not.toHaveBeenCalled();
252
+ });
253
+ it('should handle all 8 permutations of metadata existence', async () => {
254
+ const mods = [
255
+ { id: 't-t-t', pkg: true, yaml: true, yml: true },
256
+ { id: 't-t-f', pkg: true, yaml: true, yml: false },
257
+ { id: 't-f-t', pkg: true, yaml: false, yml: true },
258
+ { id: 't-f-f', pkg: true, yaml: false, yml: false },
259
+ { id: 'f-t-t', pkg: false, yaml: true, yml: true },
260
+ { id: 'f-t-f', pkg: false, yaml: true, yml: false },
261
+ { id: 'f-f-t', pkg: false, yaml: false, yml: true },
262
+ { id: 'f-f-f', pkg: false, yaml: false, yml: false },
263
+ ];
264
+
265
+ (fs.pathExists as any).mockImplementation((p: string) => {
266
+ const pStr = p.toString();
267
+ if (pStr.endsWith('modules')) return true;
268
+ const mod = mods.find((m) => pStr.includes(m.id));
269
+ if (!mod) return false;
270
+ if (pStr.endsWith('package.json')) return mod.pkg;
271
+ if (pStr.endsWith('module.yaml')) return mod.yaml;
272
+ if (pStr.endsWith('module.yml')) return mod.yml;
273
+ return true; // The directory itself
274
+ });
275
+
276
+ (fs.readdir as any).mockImplementation((p: string) => {
277
+ if (p.includes('apps/backend/modules')) return mods.map((m) => m.id);
278
+ return [];
279
+ });
280
+ (fs.stat as any).mockResolvedValue({ isDirectory: () => true });
281
+
282
+ (fs.readJson as any).mockImplementation((p: string) => {
283
+ const mod = mods.find((m) => p.includes(m.id));
284
+ if (mod?.pkg) return { version: `pkg-${mod.id}` };
285
+ return {};
286
+ });
287
+
288
+ (fs.readFile as any).mockImplementation((p: string) => {
289
+ const mod = mods.find((m) => p.includes(m.id));
290
+ if (!mod) return '';
291
+ const isYaml = p.endsWith('module.yaml');
292
+ const name = isYaml ? `yaml-${mod.id}` : `yml-${mod.id}`;
293
+ const version = isYaml ? `v-yaml-${mod.id}` : `v-yml-${mod.id}`;
294
+ return `name: ${name}\nversion: ${version}`;
295
+ });
296
+
297
+ await command.run();
298
+
299
+ // Verify specific precedence
300
+ expect(console.table).toHaveBeenCalledWith(
301
+ expect.arrayContaining([
302
+ expect.objectContaining({ name: 'yaml-t-t-t', version: 'pkg-t-t-t' }), // pkg version precedence, yaml name precedence
303
+ expect.objectContaining({ name: 'yml-f-f-t', version: 'v-yml-f-f-t' }), // yml used if yaml/pkg missing
304
+ expect.objectContaining({ name: 'f-f-f', version: 'unknown' }), // none
305
+ ]),
306
+ );
307
+ });
308
+
309
+ it('should cover catch blocks and falsy returns in all paths', async () => {
310
+ (fs.pathExists as any).mockImplementation((p: string) => {
311
+ const pStr = p.toString();
312
+ if (pStr.includes('apps/backend/modules')) return true;
313
+ return true; // Every candidate exists to trigger catch blocks
314
+ });
315
+ (fs.readdir as any).mockImplementation((p: string) => {
316
+ if (p.includes('apps/backend/modules')) return ['fail-json', 'fail-yaml'];
317
+ return [];
318
+ });
319
+ (fs.stat as any).mockResolvedValue({ isDirectory: () => true });
320
+
321
+ (fs.readJson as any).mockImplementation((p: string) => {
322
+ if (p.includes('fail-json')) throw new Error('json fail');
323
+ return null;
324
+ });
325
+ (fs.readFile as any).mockImplementation((p: string) => {
326
+ if (p.includes('fail-yaml')) throw new Error('yaml fail');
327
+ return '';
328
+ });
329
+
330
+ await command.run();
331
+ expect(console.table).toHaveBeenCalledWith(
332
+ expect.arrayContaining([
333
+ expect.objectContaining({ name: 'fail-json', version: 'unknown' }),
334
+ expect.objectContaining({ name: 'fail-yaml', version: 'unknown' }),
335
+ ]),
336
+ );
337
+ });
133
338
  });
@@ -178,4 +178,34 @@ describe('ModuleRemoveCommand', () => {
178
178
  expect(fs.readFile).not.toHaveBeenCalled();
179
179
  expect(command.success).toHaveBeenCalledWith(expect.stringContaining('removed successfully'));
180
180
  });
181
+ it('should handle missing modules key in config', async () => {
182
+ (fs.pathExists as any).mockImplementation((p: string) => true);
183
+ (fs.readFile as any).mockResolvedValue('key: value');
184
+
185
+ await command.run({ name: 'test-mod' });
186
+ expect(fs.writeFile).not.toHaveBeenCalled();
187
+ });
188
+
189
+ it('should do nothing if module not found in config lists', async () => {
190
+ (fs.pathExists as any).mockImplementation((p: string) => true);
191
+ (fs.readFile as any).mockResolvedValue('modules:\n backend:\n - existing-mod');
192
+
193
+ await command.run({ name: 'other-mod' });
194
+ expect(fs.writeFile).not.toHaveBeenCalled();
195
+ });
196
+
197
+ it('should handle empty or null config from YAML.parse', async () => {
198
+ (fs.pathExists as any).mockImplementation((p: string) => true);
199
+ (fs.readFile as any).mockResolvedValue(''); // YAML.parse('') -> null
200
+
201
+ await command.run({ name: 'test-mod' });
202
+ expect(fs.writeFile).not.toHaveBeenCalled();
203
+ });
204
+ it('should handle legacy array without the module to remove', async () => {
205
+ (fs.pathExists as any).mockImplementation((p: string) => true);
206
+ (fs.readFile as any).mockResolvedValue('modules:\n - other-mod');
207
+
208
+ await command.run({ name: 'test-mod' });
209
+ expect(fs.writeFile).not.toHaveBeenCalled();
210
+ });
181
211
  });
@@ -316,4 +316,11 @@ describe('RunCommand', () => {
316
316
  expect.stringContaining('Failed to read package.json at /mock/root: String error'),
317
317
  );
318
318
  });
319
+
320
+ it('should error if module not found', async () => {
321
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
322
+ (fs.pathExists as unknown as { mockResolvedValue: any }).mockResolvedValue(false);
323
+ await command.run({ script: 'nonexistent:sync', args: [] });
324
+ expect(command.error).toHaveBeenCalledWith('Module nonexistent not found.');
325
+ });
319
326
  });
@@ -103,7 +103,7 @@ describe('SetupCommand', () => {
103
103
  vi.mocked(fs.lstatSync).mockReturnValue({ isSymbolicLink: () => true } as unknown as any);
104
104
 
105
105
  const error = new Error('Permission denied');
106
-
106
+
107
107
  (error as unknown as { code: string }).code = 'EACCES';
108
108
  vi.mocked(fs.removeSync).mockImplementationOnce(() => {
109
109
  throw error;
@@ -114,6 +114,48 @@ describe('SetupCommand', () => {
114
114
  expect(command.error).toHaveBeenCalledWith(expect.stringContaining('Failed to symlink'));
115
115
  });
116
116
 
117
+ it('should ignore ENOENT error during removal', async () => {
118
+ vi.mocked(fs.existsSync).mockReturnValue(true);
119
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
+ vi.mocked(fs.lstatSync).mockReturnValue({ isSymbolicLink: () => true } as unknown as any);
121
+
122
+ const error = new Error('Not found');
123
+ (error as unknown as { code: string }).code = 'ENOENT';
124
+ vi.mocked(fs.removeSync).mockImplementationOnce(() => {
125
+ throw error;
126
+ });
127
+
128
+ await command.run();
129
+
130
+ // Should not log error in outer block as it was re-thrown only for non-ENOENT
131
+ // Wait, if it's NOT re-thrown (ENOENT), it continues to symlink.
132
+ expect(fs.symlink).toHaveBeenCalled();
133
+ });
134
+
135
+ it('should re-throw non-object as error during removal', async () => {
136
+ vi.mocked(fs.existsSync).mockReturnValue(true);
137
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
138
+ vi.mocked(fs.lstatSync).mockReturnValue({ isSymbolicLink: () => true } as unknown as any);
139
+ vi.mocked(fs.removeSync).mockImplementationOnce(() => {
140
+ throw 'string fail';
141
+ });
142
+
143
+ await command.run();
144
+ expect(command.error).toHaveBeenCalledWith(expect.stringContaining('string fail'));
145
+ });
146
+
147
+ it('should re-throw error without code during removal', async () => {
148
+ vi.mocked(fs.existsSync).mockReturnValue(true);
149
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
150
+ vi.mocked(fs.lstatSync).mockReturnValue({ isSymbolicLink: () => true } as unknown as any);
151
+ vi.mocked(fs.removeSync).mockImplementationOnce(() => {
152
+ throw new Error('No code');
153
+ });
154
+
155
+ await command.run();
156
+ expect(command.error).toHaveBeenCalledWith(expect.stringContaining('No code'));
157
+ });
158
+
117
159
  it('should log error if symlink fails with Error object', async () => {
118
160
  vi.mocked(fs.existsSync).mockReturnValue(true);
119
161
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -223,5 +223,46 @@ describe('ProviderRegistry', () => {
223
223
  expect.stringContaining('Failed to load local provider'),
224
224
  );
225
225
  });
226
+
227
+ it('should handle non-Error exceptions when loading local provider', async () => {
228
+ const mockRoot = '/mock/root';
229
+ vi.spyOn(fs, 'readdir').mockResolvedValue(['broken.ts'] as any);
230
+ mockJitiRequest.mockRejectedValue('String fail');
231
+
232
+ await registry.loadLocalProviders(mockRoot);
233
+ expect(logger.warn).toHaveBeenCalledWith(
234
+ expect.stringContaining('Failed to load local provider from broken.ts: String fail'),
235
+ );
236
+ });
237
+ });
238
+
239
+ describe('Core non-Error cases', () => {
240
+ it('should handle non-Error in loadCoreProviders readdir', async () => {
241
+ vi.spyOn(fs, 'access').mockResolvedValue(undefined);
242
+ vi.spyOn(fs, 'readdir').mockRejectedValue('Readdir string fail');
243
+ await registry.loadCoreProviders();
244
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('Readdir string fail'));
245
+ });
246
+
247
+ it('should handle non-Error during registration in loadCoreProviders', async () => {
248
+ vi.spyOn(fs, 'access').mockResolvedValue(undefined);
249
+ vi.spyOn(fs, 'readdir').mockResolvedValue(['fail-registration.js'] as any);
250
+
251
+ // Mock registerProviderFromModule to throw a string
252
+ const regSpy = vi
253
+ .spyOn(registry as any, 'registerProviderFromModule')
254
+ .mockImplementation(() => {
255
+ throw 'Registration string fail';
256
+ });
257
+
258
+ // Mock path.join to return a known string for vi.doMock
259
+ vi.spyOn(path, 'join').mockReturnValue('MOCK_REG_FAIL');
260
+ vi.doMock('MOCK_REG_FAIL', () => ({ default: {} }));
261
+
262
+ await registry.loadCoreProviders();
263
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('Registration string fail'));
264
+
265
+ regSpy.mockRestore();
266
+ });
226
267
  });
227
268
  });
@@ -51,15 +51,59 @@ describe('git utils', () => {
51
51
  });
52
52
 
53
53
  it('should clone repository', async () => {
54
+ // Mock anonymous to fail to test fallback
55
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
56
+ callback(new Error('Anonymous failed'), '', '');
57
+ });
58
+
54
59
  await git.clone('http://repo.git', 'dest', { recursive: true });
55
60
  expect(runCommand).toHaveBeenCalledWith('git clone --recursive http://repo.git .', 'dest');
56
61
  });
57
62
 
58
63
  it('should clone repository with depth', async () => {
64
+ // Mock anonymous to fail to test fallback
65
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
66
+ callback(new Error('Anonymous failed'), '', '');
67
+ });
68
+
59
69
  await git.clone('http://repo.git', 'dest', { depth: 1 });
60
70
  expect(runCommand).toHaveBeenCalledWith('git clone --depth 1 http://repo.git .', 'dest');
61
71
  });
62
72
 
73
+ it('should try anonymous clone first', async () => {
74
+ // Mock anonymous to succeed
75
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
76
+ callback(null, 'Done', '');
77
+ });
78
+
79
+ await git.clone('http://repo.git', 'dest');
80
+ expect(mocks.exec).toHaveBeenCalledWith(
81
+ expect.stringContaining('-c credential.helper='),
82
+ expect.any(Object),
83
+ expect.any(Function),
84
+ );
85
+ expect(runCommand).not.toHaveBeenCalled();
86
+ });
87
+
88
+ it('should handle anonymous clone with empty stdout', async () => {
89
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
90
+ callback(null, '', '');
91
+ });
92
+
93
+ await git.clone('http://repo.git', 'dest');
94
+ expect(mocks.exec).toHaveBeenCalled();
95
+ });
96
+
97
+ it('should handle anonymous clone with non-Error exception', async () => {
98
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ callback('String error' as any, '', '');
101
+ });
102
+
103
+ await git.clone('http://repo.git', 'dest');
104
+ expect(runCommand).toHaveBeenCalled();
105
+ });
106
+
63
107
  it('should update submodules', async () => {
64
108
  await git.updateSubmodules('dest');
65
109
  expect(runCommand).toHaveBeenCalledWith(
@@ -111,6 +155,50 @@ describe('git utils', () => {
111
155
  const url = await git.getRemoteUrl('cwd');
112
156
  expect(url).toBe('');
113
157
  });
158
+
159
+ it('should add submodule', async () => {
160
+ // Mock anonymous to fail
161
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
162
+ callback(new Error('Anonymous failed'), '', '');
163
+ });
164
+
165
+ await git.addSubmodule('url', 'path', 'cwd');
166
+ expect(runCommand).toHaveBeenCalledWith('git submodule add url path', 'cwd');
167
+ });
168
+
169
+ it('should try anonymous submodule add first', async () => {
170
+ // Mock anonymous to succeed
171
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
172
+ callback(null, 'Done', '');
173
+ });
174
+
175
+ await git.addSubmodule('url', 'path', 'cwd');
176
+ expect(mocks.exec).toHaveBeenCalledWith(
177
+ expect.stringContaining('-c credential.helper='),
178
+ expect.any(Object),
179
+ expect.any(Function),
180
+ );
181
+ expect(runCommand).not.toHaveBeenCalled();
182
+ });
183
+
184
+ it('should handle anonymous submodule add with empty stdout', async () => {
185
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
186
+ callback(null, '', '');
187
+ });
188
+
189
+ await git.addSubmodule('url', 'path', 'cwd');
190
+ expect(mocks.exec).toHaveBeenCalled();
191
+ });
192
+
193
+ it('should handle anonymous submodule add with non-Error exception', async () => {
194
+ mocks.exec.mockImplementationOnce((_cmd, _options, callback) => {
195
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
196
+ callback('String error' as any, '', '');
197
+ });
198
+
199
+ await git.addSubmodule('url', 'path', 'cwd');
200
+ expect(runCommand).toHaveBeenCalled();
201
+ });
114
202
  });
115
203
 
116
204
  it('should add all files', async () => {
@@ -15,6 +15,13 @@ vi.mock('node:child_process', () => ({
15
15
 
16
16
  describe('git utils', () => {
17
17
  it('should call git clone', async () => {
18
+ const { exec } = await import('node:child_process');
19
+ // Mock anonymous failure
20
+ vi.mocked(exec).mockImplementationOnce(((cmd: string, opts: any, callback: any) => {
21
+ callback(new Error('fail'), '', '');
22
+ return {} as any;
23
+ }) as any);
24
+
18
25
  await git.clone('url', 'dest');
19
26
  expect(runCommand).toHaveBeenCalledWith(expect.stringContaining('git clone'), 'dest');
20
27
  });
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/git.ts"],"sourcesContent":["import { logger, runCommand } from '@nexical/cli-core';\nimport { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\n\nexport async function clone(\n url: string,\n destination: string,\n options: { recursive?: boolean; depth?: number } = {},\n): Promise<void> {\n const { recursive = false, depth } = options;\n const cmd = `git clone ${recursive ? '--recursive ' : ''}${depth ? `--depth ${depth} ` : ''}${url} .`;\n logger.debug(`Git clone: ${url} to ${destination}`);\n await runCommand(cmd, destination);\n}\n\nexport async function getRemoteUrl(cwd: string, remote = 'origin'): Promise<string> {\n try {\n const { stdout } = await execAsync(`git remote get-url ${remote}`, { cwd });\n return stdout.trim();\n } catch (e) {\n console.error('getRemoteUrl failed:', e);\n return '';\n }\n}\n\nexport async function updateSubmodules(cwd: string): Promise<void> {\n logger.debug(`Updating submodules in ${cwd}`);\n await runCommand(\n 'git submodule foreach --recursive \"git checkout main && git pull origin main\"',\n cwd,\n );\n}\n\nexport async function checkoutOrphan(branch: string, cwd: string): Promise<void> {\n await runCommand(`git checkout --orphan ${branch}`, cwd);\n}\n\nexport async function addAll(cwd: string): Promise<void> {\n await runCommand('git add -A', cwd);\n}\n\nexport async function commit(message: string, cwd: string): Promise<void> {\n // Escape quotes in message if needed, for now assuming simple messages\n await runCommand(`git commit -m \"${message}\"`, cwd);\n}\n\nexport async function deleteBranch(branch: string, cwd: string): Promise<void> {\n await runCommand(`git branch -D ${branch}`, cwd);\n}\n\nexport async function renameBranch(branch: string, cwd: string): Promise<void> {\n await runCommand(`git branch -m ${branch}`, cwd);\n}\n\nexport async function removeRemote(remote: string, cwd: string): Promise<void> {\n await runCommand(`git remote remove ${remote}`, cwd);\n}\n\nexport async function renameRemote(oldName: string, newName: string, cwd: string): Promise<void> {\n await runCommand(`git remote rename ${oldName} ${newName}`, cwd);\n}\n\nexport async function branchExists(branch: string, cwd: string): Promise<boolean> {\n try {\n await execAsync(`git show-ref --verify --quiet refs/heads/${branch}`, { cwd });\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;AAAA;AAAA,SAAS,QAAQ,kBAAkB;AACnC,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAE1B,IAAM,YAAY,UAAU,IAAI;AAEhC,eAAsB,MACpB,KACA,aACA,UAAmD,CAAC,GACrC;AACf,QAAM,EAAE,YAAY,OAAO,MAAM,IAAI;AACrC,QAAM,MAAM,aAAa,YAAY,iBAAiB,EAAE,GAAG,QAAQ,WAAW,KAAK,MAAM,EAAE,GAAG,GAAG;AACjG,SAAO,MAAM,cAAc,GAAG,OAAO,WAAW,EAAE;AAClD,QAAM,WAAW,KAAK,WAAW;AACnC;AAEA,eAAsB,aAAa,KAAa,SAAS,UAA2B;AAClF,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,UAAU,sBAAsB,MAAM,IAAI,EAAE,IAAI,CAAC;AAC1E,WAAO,OAAO,KAAK;AAAA,EACrB,SAAS,GAAG;AACV,YAAQ,MAAM,wBAAwB,CAAC;AACvC,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,iBAAiB,KAA4B;AACjE,SAAO,MAAM,0BAA0B,GAAG,EAAE;AAC5C,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,eAAe,QAAgB,KAA4B;AAC/E,QAAM,WAAW,yBAAyB,MAAM,IAAI,GAAG;AACzD;AAEA,eAAsB,OAAO,KAA4B;AACvD,QAAM,WAAW,cAAc,GAAG;AACpC;AAEA,eAAsB,OAAO,SAAiB,KAA4B;AAExE,QAAM,WAAW,kBAAkB,OAAO,KAAK,GAAG;AACpD;AAEA,eAAsB,aAAa,QAAgB,KAA4B;AAC7E,QAAM,WAAW,iBAAiB,MAAM,IAAI,GAAG;AACjD;AAEA,eAAsB,aAAa,QAAgB,KAA4B;AAC7E,QAAM,WAAW,iBAAiB,MAAM,IAAI,GAAG;AACjD;AAEA,eAAsB,aAAa,QAAgB,KAA4B;AAC7E,QAAM,WAAW,qBAAqB,MAAM,IAAI,GAAG;AACrD;AAEA,eAAsB,aAAa,SAAiB,SAAiB,KAA4B;AAC/F,QAAM,WAAW,qBAAqB,OAAO,IAAI,OAAO,IAAI,GAAG;AACjE;AAEA,eAAsB,aAAa,QAAgB,KAA+B;AAChF,MAAI;AACF,UAAM,UAAU,4CAA4C,MAAM,IAAI,EAAE,IAAI,CAAC;AAC7E,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}