workon 3.2.2 → 3.2.4

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/cli.js CHANGED
@@ -11,18 +11,99 @@ var __export = (target, all) => {
11
11
 
12
12
  // src/lib/config.ts
13
13
  import Conf from "conf";
14
- var TRANSIENT_PROPS, Config;
14
+ import { openSync, closeSync, unlinkSync, existsSync, mkdirSync } from "fs";
15
+ import { dirname } from "path";
16
+ var TRANSIENT_PROPS, FileLock, Config;
15
17
  var init_config = __esm({
16
18
  "src/lib/config.ts"() {
17
19
  "use strict";
18
20
  TRANSIENT_PROPS = ["pkg", "work"];
19
- Config = class {
21
+ FileLock = class _FileLock {
22
+ lockPath;
23
+ fd = null;
24
+ static LOCK_TIMEOUT_MS = 5e3;
25
+ static RETRY_INTERVAL_MS = 50;
26
+ constructor(configPath) {
27
+ this.lockPath = `${configPath}.lock`;
28
+ }
29
+ async acquire() {
30
+ const startTime = Date.now();
31
+ const lockDir = dirname(this.lockPath);
32
+ if (!existsSync(lockDir)) {
33
+ mkdirSync(lockDir, { recursive: true });
34
+ }
35
+ while (Date.now() - startTime < _FileLock.LOCK_TIMEOUT_MS) {
36
+ try {
37
+ this.fd = openSync(this.lockPath, "wx");
38
+ return;
39
+ } catch (error) {
40
+ if (error.code === "EEXIST") {
41
+ try {
42
+ const stat = await import("fs").then((fs) => fs.promises.stat(this.lockPath));
43
+ const age = Date.now() - stat.mtimeMs;
44
+ if (age > _FileLock.LOCK_TIMEOUT_MS) {
45
+ try {
46
+ unlinkSync(this.lockPath);
47
+ } catch {
48
+ }
49
+ }
50
+ } catch {
51
+ }
52
+ await new Promise((resolve2) => setTimeout(resolve2, _FileLock.RETRY_INTERVAL_MS));
53
+ } else {
54
+ throw error;
55
+ }
56
+ }
57
+ }
58
+ throw new Error("Failed to acquire config lock: timeout");
59
+ }
60
+ release() {
61
+ if (this.fd !== null) {
62
+ try {
63
+ closeSync(this.fd);
64
+ } catch {
65
+ }
66
+ this.fd = null;
67
+ }
68
+ try {
69
+ unlinkSync(this.lockPath);
70
+ } catch {
71
+ }
72
+ }
73
+ };
74
+ Config = class _Config {
75
+ static _instance = null;
20
76
  _transient = {};
77
+ // Using definite assignment assertion since singleton pattern may return existing instance
21
78
  _store;
79
+ _lock;
22
80
  constructor() {
81
+ if (_Config._instance && process.env.NODE_ENV !== "test") {
82
+ return _Config._instance;
83
+ }
23
84
  this._store = new Conf({
24
- projectName: "workon"
85
+ projectName: "workon",
86
+ ...process.env.WORKON_CONFIG_DIR && { cwd: process.env.WORKON_CONFIG_DIR }
25
87
  });
88
+ this._lock = new FileLock(this._store.path);
89
+ if (process.env.NODE_ENV !== "test") {
90
+ _Config._instance = this;
91
+ }
92
+ }
93
+ /**
94
+ * Get the singleton instance (creates one if needed)
95
+ */
96
+ static getInstance() {
97
+ if (!_Config._instance) {
98
+ _Config._instance = new _Config();
99
+ }
100
+ return _Config._instance;
101
+ }
102
+ /**
103
+ * Reset the singleton instance (for testing purposes)
104
+ */
105
+ static resetInstance() {
106
+ _Config._instance = null;
26
107
  }
27
108
  get(key, defaultValue) {
28
109
  const rootKey = key.split(".")[0];
@@ -37,10 +118,9 @@ var init_config = __esm({
37
118
  this._transient[key] = value;
38
119
  } else {
39
120
  if (value === void 0) {
40
- this._store.set(key, value);
41
- } else {
42
- this._store.set(key, value);
121
+ throw new Error(`Cannot set '${key}' to undefined. Use delete() to remove keys.`);
43
122
  }
123
+ this._store.set(key, value);
44
124
  }
45
125
  }
46
126
  has(key) {
@@ -58,6 +138,9 @@ var init_config = __esm({
58
138
  this._store.delete(key);
59
139
  }
60
140
  }
141
+ /**
142
+ * Get all projects. Returns a fresh copy from the store.
143
+ */
61
144
  getProjects() {
62
145
  return this.get("projects") ?? {};
63
146
  }
@@ -65,15 +148,50 @@ var init_config = __esm({
65
148
  const projects = this.getProjects();
66
149
  return projects[name];
67
150
  }
151
+ /**
152
+ * Set a project with file locking to prevent race conditions.
153
+ * This ensures atomic read-modify-write operations.
154
+ */
155
+ async setProjectSafe(name, config) {
156
+ await this._lock.acquire();
157
+ try {
158
+ const freshProjects = this._store.get("projects") ?? {};
159
+ freshProjects[name] = config;
160
+ this._store.set("projects", freshProjects);
161
+ } finally {
162
+ this._lock.release();
163
+ }
164
+ }
165
+ /**
166
+ * Synchronous version of setProject for backwards compatibility.
167
+ * Note: This is less safe than setProjectSafe() in concurrent scenarios.
168
+ * Consider migrating to setProjectSafe() for critical operations.
169
+ */
68
170
  setProject(name, config) {
69
- const projects = this.getProjects();
70
- projects[name] = config;
71
- this.set("projects", projects);
171
+ const freshProjects = this._store.get("projects") ?? {};
172
+ freshProjects[name] = config;
173
+ this._store.set("projects", freshProjects);
174
+ }
175
+ /**
176
+ * Delete a project with file locking to prevent race conditions.
177
+ */
178
+ async deleteProjectSafe(name) {
179
+ await this._lock.acquire();
180
+ try {
181
+ const freshProjects = this._store.get("projects") ?? {};
182
+ delete freshProjects[name];
183
+ this._store.set("projects", freshProjects);
184
+ } finally {
185
+ this._lock.release();
186
+ }
72
187
  }
188
+ /**
189
+ * Synchronous version of deleteProject for backwards compatibility.
190
+ */
73
191
  deleteProject(name) {
74
- const projects = this.getProjects();
75
- delete projects[name];
76
- this.set("projects", projects);
192
+ const freshProjects = this._store.get("projects") ?? {};
193
+ delete freshProjects[name];
194
+ this._store.set("projects", freshProjects);
77
195
  }
78
196
  getDefaults() {
79
197
  return this.get("project_defaults");
@@ -280,7 +398,7 @@ var init_environment = __esm({
280
398
  }
281
399
  static ensureConfigured() {
282
400
  if (!this.configured) {
283
- this.config = new Config();
401
+ this.config = Config.getInstance();
284
402
  this.log = {
285
403
  debug: () => {
286
404
  },
@@ -632,10 +750,10 @@ var init_claude = __esm({
632
750
  }
633
751
  static getClaudeCommand(config) {
634
752
  if (typeof config === "boolean" || config === void 0) {
635
- return "claude";
753
+ return "claude --dangerously-skip-permissions";
636
754
  }
637
755
  const flags = config.flags || [];
638
- return flags.length > 0 ? `claude ${flags.join(" ")}` : "claude";
756
+ return flags.length > 0 ? `claude --dangerously-skip-permissions ${flags.join(" ")}` : "claude --dangerously-skip-permissions";
639
757
  }
640
758
  static get processing() {
641
759
  return {
@@ -1116,6 +1234,9 @@ var init_sanitize = __esm({
1116
1234
  // src/lib/tmux.ts
1117
1235
  import { exec as execCallback, spawn as spawn7 } from "child_process";
1118
1236
  import { promisify } from "util";
1237
+ function wrapWithShellFallback(command) {
1238
+ return `${command}; exec $SHELL`;
1239
+ }
1119
1240
  var exec, TmuxManager;
1120
1241
  var init_tmux = __esm({
1121
1242
  "src/lib/tmux.ts"() {
@@ -1159,9 +1280,9 @@ var init_tmux = __esm({
1159
1280
  await this.killSession(sessionName);
1160
1281
  }
1161
1282
  const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
1162
- const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
1283
+ const wrappedClaudeCmd = escapeForSingleQuotes(wrapWithShellFallback(claudeCommand));
1163
1284
  await exec(
1164
- `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`
1285
+ `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${wrappedClaudeCmd}'`
1165
1286
  );
1166
1287
  await exec(`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`);
1167
1288
  await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
@@ -1175,16 +1296,15 @@ var init_tmux = __esm({
1175
1296
  await this.killSession(sessionName);
1176
1297
  }
1177
1298
  const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
1178
- const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
1179
- const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
1299
+ const wrappedClaudeCmd = escapeForSingleQuotes(wrapWithShellFallback(claudeCommand));
1300
+ const wrappedNpmCmd = escapeForSingleQuotes(wrapWithShellFallback(npmCommand));
1180
1301
  await exec(
1181
- `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`
1302
+ `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${wrappedClaudeCmd}'`
1182
1303
  );
1183
1304
  await exec(`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`);
1184
1305
  await exec(
1185
- `tmux split-window -v -t '${escapedSession}:0.1' -c '${escapedPath}' '${escapedNpmCmd}'`
1306
+ `tmux split-window -v -t '${escapedSession}:0.1' -c '${escapedPath}' '${wrappedNpmCmd}'`
1186
1307
  );
1187
- await exec(`tmux set-option -t '${escapedSession}:0.2' remain-on-exit on`);
1188
1308
  await exec(`tmux resize-pane -t '${escapedSession}:0.2' -y 10`);
1189
1309
  await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
1190
1310
  return sessionName;
@@ -1193,15 +1313,14 @@ var init_tmux = __esm({
1193
1313
  const sessionName = this.getSessionName(projectName);
1194
1314
  const escapedSession = escapeForSingleQuotes(sessionName);
1195
1315
  const escapedPath = escapeForSingleQuotes(projectPath);
1196
- const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
1316
+ const wrappedNpmCmd = escapeForSingleQuotes(wrapWithShellFallback(npmCommand));
1197
1317
  if (await this.sessionExists(sessionName)) {
1198
1318
  await this.killSession(sessionName);
1199
1319
  }
1200
1320
  await exec(`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}'`);
1201
1321
  await exec(
1202
- `tmux split-window -h -t '${escapedSession}' -c '${escapedPath}' '${escapedNpmCmd}'`
1322
+ `tmux split-window -h -t '${escapedSession}' -c '${escapedPath}' '${wrappedNpmCmd}'`
1203
1323
  );
1204
- await exec(`tmux set-option -t '${escapedSession}:0.1' remain-on-exit on`);
1205
1324
  await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
1206
1325
  return sessionName;
1207
1326
  }
@@ -1230,11 +1349,11 @@ var init_tmux = __esm({
1230
1349
  const escapedSession = escapeForSingleQuotes(sessionName);
1231
1350
  const escapedPath = escapeForSingleQuotes(projectPath);
1232
1351
  const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
1233
- const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
1352
+ const wrappedClaudeCmd = escapeForSingleQuotes(wrapWithShellFallback(claudeCommand));
1234
1353
  return [
1235
1354
  `# Create tmux split session for ${sanitizeForShell(projectName)}`,
1236
1355
  `tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
1237
- `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`,
1356
+ `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${wrappedClaudeCmd}'`,
1238
1357
  `tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`,
1239
1358
  `tmux select-pane -t '${escapedSession}:0.0'`,
1240
1359
  this.getAttachCommand(sessionName)
@@ -1245,15 +1364,14 @@ var init_tmux = __esm({
1245
1364
  const escapedSession = escapeForSingleQuotes(sessionName);
1246
1365
  const escapedPath = escapeForSingleQuotes(projectPath);
1247
1366
  const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
1248
- const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
1249
- const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
1367
+ const wrappedClaudeCmd = escapeForSingleQuotes(wrapWithShellFallback(claudeCommand));
1368
+ const wrappedNpmCmd = escapeForSingleQuotes(wrapWithShellFallback(npmCommand));
1250
1369
  return [
1251
1370
  `# Create tmux three-pane session for ${sanitizeForShell(projectName)}`,
1252
1371
  `tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
1253
- `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`,
1372
+ `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${wrappedClaudeCmd}'`,
1254
1373
  `tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`,
1255
- `tmux split-window -v -t '${escapedSession}:0.1' -c '${escapedPath}' '${escapedNpmCmd}'`,
1256
- `tmux set-option -t '${escapedSession}:0.2' remain-on-exit on`,
1374
+ `tmux split-window -v -t '${escapedSession}:0.1' -c '${escapedPath}' '${wrappedNpmCmd}'`,
1257
1375
  `tmux resize-pane -t '${escapedSession}:0.2' -y 10`,
1258
1376
  `tmux select-pane -t '${escapedSession}:0.0'`,
1259
1377
  this.getAttachCommand(sessionName)
@@ -1263,13 +1381,12 @@ var init_tmux = __esm({
1263
1381
  const sessionName = this.getSessionName(projectName);
1264
1382
  const escapedSession = escapeForSingleQuotes(sessionName);
1265
1383
  const escapedPath = escapeForSingleQuotes(projectPath);
1266
- const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
1384
+ const wrappedNpmCmd = escapeForSingleQuotes(wrapWithShellFallback(npmCommand));
1267
1385
  return [
1268
1386
  `# Create tmux two-pane session with npm for ${sanitizeForShell(projectName)}`,
1269
1387
  `tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
1270
1388
  `tmux new-session -d -s '${escapedSession}' -c '${escapedPath}'`,
1271
- `tmux split-window -h -t '${escapedSession}' -c '${escapedPath}' '${escapedNpmCmd}'`,
1272
- `tmux set-option -t '${escapedSession}:0.1' remain-on-exit on`,
1389
+ `tmux split-window -h -t '${escapedSession}' -c '${escapedPath}' '${wrappedNpmCmd}'`,
1273
1390
  `tmux select-pane -t '${escapedSession}:0.0'`,
1274
1391
  this.getAttachCommand(sessionName)
1275
1392
  ];
@@ -1469,8 +1586,7 @@ async function initProject(defaultName, fromUser, ctx) {
1469
1586
  ide,
1470
1587
  events
1471
1588
  };
1472
- projects[name] = projectConfig;
1473
- config.set("projects", projects);
1589
+ await config.setProjectSafe(name, projectConfig);
1474
1590
  log.info("Your project has been initialized.");
1475
1591
  log.info(`Use 'workon ${name}' to start working!`);
1476
1592
  }
@@ -1491,8 +1607,7 @@ async function initBranch(defaultName, ctx) {
1491
1607
  branch
1492
1608
  });
1493
1609
  const branchConfig = mergedConfig;
1494
- projects[branchName] = branchConfig;
1495
- config.set("projects", projects);
1610
+ await config.setProjectSafe(branchName, branchConfig);
1496
1611
  log.info("Your branch configuration has been initialized.");
1497
1612
  log.info(`Use 'workon ${branchName}' to start working!`);
1498
1613
  }
@@ -1690,7 +1805,7 @@ async function createProjectManage(ctx) {
1690
1805
  default: true
1691
1806
  });
1692
1807
  if (confirmed) {
1693
- config.setProject(name, projectConfig);
1808
+ await config.setProjectSafe(name, projectConfig);
1694
1809
  log.info(`Project '${name}' created successfully!`);
1695
1810
  }
1696
1811
  }
@@ -1768,7 +1883,7 @@ async function editProjectManage(ctx) {
1768
1883
  default: true
1769
1884
  });
1770
1885
  if (confirmed) {
1771
- config.setProject(name, updatedConfig);
1886
+ await config.setProjectSafe(name, updatedConfig);
1772
1887
  log.info(`Project '${name}' updated successfully!`);
1773
1888
  }
1774
1889
  }
@@ -1794,7 +1909,7 @@ async function deleteProjectManage(ctx) {
1794
1909
  });
1795
1910
  if (deleteAll) {
1796
1911
  for (const branch of branches) {
1797
- config.deleteProject(branch);
1912
+ await config.deleteProjectSafe(branch);
1798
1913
  }
1799
1914
  }
1800
1915
  }
@@ -1803,7 +1918,7 @@ async function deleteProjectManage(ctx) {
1803
1918
  default: false
1804
1919
  });
1805
1920
  if (confirmed) {
1806
- config.deleteProject(name);
1921
+ await config.deleteProjectSafe(name);
1807
1922
  log.info(`Project '${name}' deleted.`);
1808
1923
  }
1809
1924
  }
@@ -1883,7 +1998,7 @@ async function editBranchManage(projectName, ctx) {
1883
1998
  default: true
1884
1999
  });
1885
2000
  if (confirmed) {
1886
- config.setProject(branchName, updatedConfig);
2001
+ await config.setProjectSafe(branchName, updatedConfig);
1887
2002
  log.info(`Branch configuration '${branchName}' updated successfully!`);
1888
2003
  }
1889
2004
  }
@@ -1908,7 +2023,7 @@ async function deleteBranchManage(projectName, ctx) {
1908
2023
  default: false
1909
2024
  });
1910
2025
  if (confirmed) {
1911
- config.deleteProject(branchName);
2026
+ await config.deleteProjectSafe(branchName);
1912
2027
  log.info(`Branch configuration '${branchName}' deleted.`);
1913
2028
  }
1914
2029
  }
@@ -1996,9 +2111,9 @@ async function processProject(projectParam, options, ctx) {
1996
2111
  const projectEnv = ProjectEnvironment.load(projectCfg, config.getDefaults());
1997
2112
  await switchTo(projectEnv, requestedCommands, options, ctx);
1998
2113
  } else {
1999
- log.debug(`Project '${projectName}' not found, starting interactive mode`);
2000
- const { runInteractive: runInteractive2 } = await Promise.resolve().then(() => (init_interactive(), interactive_exports));
2001
- await runInteractive2({ config, log, environment, suggestedName: projectName });
2114
+ log.error(`Project '${projectName}' not found.`);
2115
+ log.info(`Run 'workon' without arguments to see available projects or create a new one.`);
2116
+ process.exit(1);
2002
2117
  }
2003
2118
  }
2004
2119
  function validateRequestedCommands(requestedCommands, projectConfig, projectName) {
@@ -2066,7 +2181,8 @@ function resolveCommandDependencies(requestedCommands, project) {
2066
2181
  }
2067
2182
  function getClaudeArgs(project) {
2068
2183
  const claudeConfig = project.events.claude;
2069
- return typeof claudeConfig === "object" && claudeConfig.flags ? claudeConfig.flags : [];
2184
+ const userFlags = typeof claudeConfig === "object" && claudeConfig.flags ? claudeConfig.flags : [];
2185
+ return ["--dangerously-skip-permissions", ...userFlags];
2070
2186
  }
2071
2187
  async function getNpmCommand(project) {
2072
2188
  const npmConfig = project.events.npm;
@@ -2271,8 +2387,8 @@ init_environment();
2271
2387
  init_registry();
2272
2388
  init_open();
2273
2389
  import { Command as Command8 } from "commander";
2274
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
2275
- import { join, dirname } from "path";
2390
+ import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
2391
+ import { join, dirname as dirname2 } from "path";
2276
2392
  import { fileURLToPath } from "url";
2277
2393
  import loog from "loog";
2278
2394
  import omelette from "omelette";
@@ -2499,7 +2615,7 @@ async function createProject(ctx) {
2499
2615
  default: true
2500
2616
  });
2501
2617
  if (confirmed) {
2502
- config.setProject(name, projectConfig);
2618
+ await config.setProjectSafe(name, projectConfig);
2503
2619
  log.info(`Project '${name}' created successfully!`);
2504
2620
  log.info(`Use 'workon ${name}' to start working!`);
2505
2621
  } else {
@@ -2589,7 +2705,7 @@ async function editProject(ctx) {
2589
2705
  default: true
2590
2706
  });
2591
2707
  if (confirmed) {
2592
- config.setProject(name, updatedConfig);
2708
+ await config.setProjectSafe(name, updatedConfig);
2593
2709
  log.info(`Project '${name}' updated successfully!`);
2594
2710
  } else {
2595
2711
  log.info("Edit cancelled.");
@@ -2612,7 +2728,7 @@ async function deleteProject(ctx) {
2612
2728
  default: false
2613
2729
  });
2614
2730
  if (confirmed) {
2615
- config.deleteProject(name);
2731
+ await config.deleteProjectSafe(name);
2616
2732
  log.info(`Project '${name}' deleted.`);
2617
2733
  } else {
2618
2734
  log.info("Delete cancelled.");
@@ -2638,7 +2754,7 @@ async function listProjects(ctx) {
2638
2754
 
2639
2755
  // src/commands/add.ts
2640
2756
  import { Command as Command7 } from "commander";
2641
- import { existsSync, readFileSync } from "fs";
2757
+ import { existsSync as existsSync2, readFileSync } from "fs";
2642
2758
  import { basename, resolve } from "path";
2643
2759
  import File6 from "phylo";
2644
2760
  import { confirm as confirm5 } from "@inquirer/prompts";
@@ -2660,7 +2776,7 @@ async function addProject(pathArg, options, ctx) {
2660
2776
  const projects = config.getProjects();
2661
2777
  const targetPath = resolve(pathArg);
2662
2778
  log.debug(`Resolved path: ${targetPath}`);
2663
- if (!existsSync(targetPath)) {
2779
+ if (!existsSync2(targetPath)) {
2664
2780
  log.error(`Path does not exist: ${targetPath}`);
2665
2781
  process.exit(1);
2666
2782
  }
@@ -2725,7 +2841,7 @@ async function addProject(pathArg, options, ctx) {
2725
2841
  if (discovery.hasClaude) {
2726
2842
  projectConfig.events.claude = true;
2727
2843
  }
2728
- config.setProject(projectName, projectConfig);
2844
+ await config.setProjectSafe(projectName, projectConfig);
2729
2845
  log.info(`Added project '${projectName}'`);
2730
2846
  log.info(` Path: ${relativePath}`);
2731
2847
  log.info(` IDE: ${ide}`);
@@ -2744,7 +2860,7 @@ function discoverProject(targetPath, log) {
2744
2860
  packageJson: null
2745
2861
  };
2746
2862
  const packageJsonPath = resolve(targetPath, "package.json");
2747
- if (existsSync(packageJsonPath)) {
2863
+ if (existsSync2(packageJsonPath)) {
2748
2864
  discovery.isNode = true;
2749
2865
  log.debug("Detected Node project (package.json found)");
2750
2866
  try {
@@ -2760,25 +2876,25 @@ function discoverProject(targetPath, log) {
2760
2876
  }
2761
2877
  }
2762
2878
  const bunLockPath = resolve(targetPath, "bun.lockb");
2763
- if (existsSync(bunLockPath)) {
2879
+ if (existsSync2(bunLockPath)) {
2764
2880
  discovery.isBun = true;
2765
2881
  log.debug("Detected Bun project (bun.lockb found)");
2766
2882
  }
2767
2883
  const vscodeDir = resolve(targetPath, ".vscode");
2768
2884
  const cursorDir = resolve(targetPath, ".cursor");
2769
2885
  const ideaDir = resolve(targetPath, ".idea");
2770
- if (existsSync(cursorDir)) {
2886
+ if (existsSync2(cursorDir)) {
2771
2887
  discovery.detectedIde = "cursor";
2772
2888
  log.debug("Detected Cursor (.cursor directory found)");
2773
- } else if (existsSync(vscodeDir)) {
2889
+ } else if (existsSync2(vscodeDir)) {
2774
2890
  discovery.detectedIde = "code";
2775
2891
  log.debug("Detected VS Code (.vscode directory found)");
2776
- } else if (existsSync(ideaDir)) {
2892
+ } else if (existsSync2(ideaDir)) {
2777
2893
  discovery.detectedIde = "idea";
2778
2894
  log.debug("Detected IntelliJ IDEA (.idea directory found)");
2779
2895
  }
2780
2896
  const claudeMdPath = resolve(targetPath, "CLAUDE.md");
2781
- if (existsSync(claudeMdPath)) {
2897
+ if (existsSync2(claudeMdPath)) {
2782
2898
  discovery.hasClaude = true;
2783
2899
  log.debug("Detected Claude Code project (CLAUDE.md found)");
2784
2900
  }
@@ -2787,7 +2903,7 @@ function discoverProject(targetPath, log) {
2787
2903
 
2788
2904
  // src/commands/index.ts
2789
2905
  var __filename = fileURLToPath(import.meta.url);
2790
- var __dirname = dirname(__filename);
2906
+ var __dirname = dirname2(__filename);
2791
2907
  function findPackageJson() {
2792
2908
  const paths = [
2793
2909
  join(__dirname, "../package.json"),
@@ -2795,7 +2911,7 @@ function findPackageJson() {
2795
2911
  join(process.cwd(), "package.json")
2796
2912
  ];
2797
2913
  for (const p of paths) {
2798
- if (existsSync2(p)) {
2914
+ if (existsSync3(p)) {
2799
2915
  return p;
2800
2916
  }
2801
2917
  }
@@ -2935,6 +3051,15 @@ workon() {
2935
3051
  }
2936
3052
 
2937
3053
  // src/cli.ts
3054
+ if (process.stdout.isTTY) {
3055
+ process.stdout.write("");
3056
+ }
2938
3057
  var program = createCli();
2939
- program.parse();
3058
+ program.parseAsync().catch((error) => {
3059
+ if (error?.name === "ExitPromptError" || error?.message?.includes("SIGINT")) {
3060
+ process.exit(130);
3061
+ }
3062
+ console.error(error);
3063
+ process.exit(1);
3064
+ });
2940
3065
  //# sourceMappingURL=cli.js.map