contextswitch 0.1.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.
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # ContextSwitch
2
+
3
+ Domain-based context management for Claude Code CLI. Switch between different project contexts while preserving session history.
4
+
5
+ ## Features
6
+
7
+ ✅ **Session Persistence** - Resume previous Claude sessions when switching domains
8
+ ✅ **Domain Management** - Organize projects with separate configurations
9
+ ✅ **MCP Server Support** - Configure Model Context Protocol servers per domain
10
+ ✅ **Cross-Platform** - Works on macOS, Linux, and Windows
11
+ ✅ **Session Archives** - Save and restore session snapshots
12
+ ✅ **Process Management** - Clean switching between Claude instances
13
+
14
+ ## Installation
15
+
16
+ ### Quick Install
17
+ ```bash
18
+ # Clone the repository
19
+ git clone https://github.com/yourusername/contextswitch.git
20
+ cd contextswitch
21
+
22
+ # Run the installation script
23
+ chmod +x install.sh
24
+ ./install.sh
25
+ ```
26
+
27
+ This sets up the `cs` command for easy use. For other installation methods, see [INSTALLATION.md](INSTALLATION.md).
28
+
29
+ ### Manual Install
30
+ ```bash
31
+ # Install dependencies
32
+ npm install
33
+
34
+ # Build the project
35
+ npm run build:node
36
+
37
+ # Add alias to your shell
38
+ echo "alias cs='node $(pwd)/dist/cli.js'" >> ~/.zshrc
39
+ source ~/.zshrc
40
+
41
+ # Now you can use 'cs' command
42
+ cs --help
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```bash
48
+ # Initialize ContextSwitch
49
+ node dist/cli.js init
50
+
51
+ # Create a new domain for your project (working directory is REQUIRED)
52
+ node dist/cli.js domain add backend --working-dir ~/projects/api
53
+
54
+ # The working directory is where Claude will operate when you switch to this domain
55
+ node dist/cli.js switch backend
56
+
57
+ # Check status
58
+ node dist/cli.js status
59
+
60
+ # List all domains
61
+ node dist/cli.js list
62
+ ```
63
+
64
+ ## Commands
65
+
66
+ ### Core Commands
67
+
68
+ - `cs init` - Initialize ContextSwitch configuration
69
+ - `cs switch <domain>` - Switch to a different domain
70
+ - `cs list` - List all available domains
71
+ - `cs status [domain]` - Show current domain and session status
72
+
73
+ ### Domain Management
74
+
75
+ - `cs domain add <name>` - Create a new domain
76
+ - `cs domain remove <name>` - Delete a domain
77
+ - `cs reset <domain>` - Reset domain session (with auto-archive)
78
+ - `cs archive <domain>` - Archive current session
79
+
80
+ ### Diagnostics
81
+
82
+ - `cs doctor` - Check system configuration
83
+ - `cs --help` - Show help information
84
+ - `cs --version` - Show version
85
+
86
+ ## Domain Configuration
87
+
88
+ Domains are stored as YAML files in the configuration directory. Example domain:
89
+
90
+ ```yaml
91
+ name: backend
92
+ workingDirectory: ~/projects/api
93
+
94
+ claudeConfig:
95
+ instructions: ./CLAUDE.md
96
+ memory:
97
+ - ./context/api-overview.md
98
+
99
+ mcpServers:
100
+ filesystem:
101
+ command: npx
102
+ args: ["-y", "@modelcontextprotocol/server-filesystem"]
103
+ env:
104
+ DIRECTORY: "./"
105
+
106
+ env:
107
+ NODE_ENV: development
108
+ ```
109
+
110
+ ## Project Structure
111
+
112
+ ```
113
+ contextswitch/
114
+ ├── src/
115
+ │ ├── cli.ts # Main CLI entry point
116
+ │ ├── commands/ # Command implementations
117
+ │ │ ├── switch.ts # Domain switching logic
118
+ │ │ ├── reset.ts # Session reset
119
+ │ │ └── archive.ts # Session archiving
120
+ │ └── core/ # Core modules
121
+ │ ├── domain.ts # Domain schemas (Zod)
122
+ │ ├── config.ts # Configuration management
123
+ │ ├── session.ts # Session ID generation
124
+ │ ├── process.ts # Process management
125
+ │ └── paths.ts # Cross-platform paths
126
+ ├── domains/ # Domain templates
127
+ ├── package.json
128
+ ├── tsconfig.json
129
+ └── README.md
130
+ ```
131
+
132
+ ## Configuration Files
133
+
134
+ - **Domains**: `~/Library/Application Support/contextswitch/domains/*.yml` (macOS)
135
+ - **State**: `~/Library/Application Support/contextswitch/state.json`
136
+ - **Archives**: `~/Library/Application Support/contextswitch/archives/`
137
+
138
+ ## Development
139
+
140
+ ```bash
141
+ # Run TypeScript compiler check
142
+ npx tsc --noEmit
143
+
144
+ # Run in development mode
145
+ npm run dev
146
+
147
+ # Build for production
148
+ npm run build:node
149
+
150
+ # Run tests (when implemented)
151
+ npm test
152
+ ```
153
+
154
+ ## Technical Details
155
+
156
+ - Built with TypeScript and Commander.js
157
+ - Uses Zod for runtime validation
158
+ - Deterministic session IDs via UUID v5
159
+ - Atomic file operations for data safety
160
+ - Cross-platform process management
161
+
162
+ ## Status
163
+
164
+ **Current Version**: 0.1.0 (MVP)
165
+
166
+ ### Implemented
167
+ - ✅ Core domain management
168
+ - ✅ Session persistence with `--resume`
169
+ - ✅ Process management (kill/spawn Claude)
170
+ - ✅ State tracking and atomic writes
171
+ - ✅ Cross-platform path resolution
172
+ - ✅ Basic CLI commands
173
+ - ✅ Archive functionality
174
+
175
+ ### Not Yet Implemented
176
+ - ⏳ Git sync for team sharing
177
+ - ⏳ Token usage estimation
178
+ - ⏳ Advanced diagnostics
179
+ - ⏳ Binary compilation with Bun
180
+ - ⏳ Automated tests
181
+ - ⏳ Shell integration scripts
182
+
183
+ ## License
184
+
185
+ MIT
@@ -0,0 +1,12 @@
1
+ import {
2
+ archiveCommand,
3
+ listArchives
4
+ } from "./chunk-2LUEUGBV.js";
5
+ import "./chunk-XGE4JP55.js";
6
+ import "./chunk-UKMZ4CUZ.js";
7
+ import "./chunk-A7YXSI66.js";
8
+ import "./chunk-PNKVD2UK.js";
9
+ export {
10
+ archiveCommand,
11
+ listArchives
12
+ };
@@ -0,0 +1,110 @@
1
+ import {
2
+ configManager
3
+ } from "./chunk-XGE4JP55.js";
4
+ import {
5
+ init_session,
6
+ session_exports
7
+ } from "./chunk-UKMZ4CUZ.js";
8
+ import {
9
+ paths
10
+ } from "./chunk-A7YXSI66.js";
11
+ import {
12
+ __toCommonJS
13
+ } from "./chunk-PNKVD2UK.js";
14
+
15
+ // src/commands/archive.ts
16
+ import picocolors from "picocolors";
17
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, statSync, readdirSync } from "fs";
18
+ import { join, basename } from "path";
19
+ var pc = picocolors;
20
+ async function archiveCommand(domainName) {
21
+ try {
22
+ if (!configManager.domainExists(domainName)) {
23
+ throw new Error(`Domain '${domainName}' not found`);
24
+ }
25
+ const state = configManager.loadState();
26
+ const session = state.sessions?.[domainName];
27
+ if (!session) {
28
+ console.log(pc.yellow(`Domain '${domainName}' has no active session to archive.`));
29
+ return;
30
+ }
31
+ console.log(pc.cyan(`\u{1F4E6} Archiving session for domain '${domainName}'...`));
32
+ const archiveDir = join(paths.archivesDir, domainName);
33
+ if (!existsSync(archiveDir)) {
34
+ mkdirSync(archiveDir, { recursive: true });
35
+ }
36
+ const timestamp = /* @__PURE__ */ new Date();
37
+ const archiveMetadata = {
38
+ domain: domainName,
39
+ session,
40
+ timestamp: timestamp.toISOString(),
41
+ config: configManager.loadDomain(domainName)
42
+ };
43
+ const dateStr = timestamp.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, -5);
44
+ const archiveName = `${dateStr}.json`;
45
+ const archivePath = join(archiveDir, archiveName);
46
+ writeFileSync(archivePath, JSON.stringify(archiveMetadata, null, 2), "utf-8");
47
+ const domain = configManager.loadDomain(domainName);
48
+ if (domain.claudeConfig?.memory) {
49
+ const memoryArchiveDir = join(archiveDir, dateStr, "memory");
50
+ mkdirSync(memoryArchiveDir, { recursive: true });
51
+ for (const memoryFile of domain.claudeConfig.memory) {
52
+ const expandedPath = paths.expandPath(memoryFile);
53
+ if (existsSync(expandedPath)) {
54
+ const fileName = basename(expandedPath);
55
+ const destPath = join(memoryArchiveDir, fileName);
56
+ const content = readFileSync(expandedPath, "utf-8");
57
+ writeFileSync(destPath, content, "utf-8");
58
+ }
59
+ }
60
+ }
61
+ const stats = statSync(archivePath);
62
+ const sizeKB = Math.round(stats.size / 1024);
63
+ console.log(pc.green(`\u2705 Session archived successfully`));
64
+ console.log(pc.gray(`Archive: ${archivePath}`));
65
+ console.log(pc.gray(`Size: ${sizeKB} KB`));
66
+ console.log(pc.gray(`Session age: ${(init_session(), __toCommonJS(session_exports)).SessionManager.getSessionAge(session.started)}`));
67
+ } catch (error) {
68
+ console.error(pc.red(`\u274C Failed to archive session: ${error}`));
69
+ process.exit(1);
70
+ }
71
+ }
72
+ function listArchives(domainName) {
73
+ const archiveDir = join(paths.archivesDir, domainName);
74
+ if (!existsSync(archiveDir)) {
75
+ console.log(pc.yellow(`No archives found for domain '${domainName}'`));
76
+ return [];
77
+ }
78
+ const files = readdirSync(archiveDir).filter((file) => file.endsWith(".json")).sort().reverse();
79
+ if (files.length === 0) {
80
+ console.log(pc.yellow(`No archives found for domain '${domainName}'`));
81
+ return [];
82
+ }
83
+ console.log(pc.cyan(`\u{1F4E6} Archives for domain '${domainName}':
84
+ `));
85
+ const archives = [];
86
+ for (const file of files.slice(0, 10)) {
87
+ const archivePath = join(archiveDir, file);
88
+ try {
89
+ const content = readFileSync(archivePath, "utf-8");
90
+ const metadata = JSON.parse(content);
91
+ const date = new Date(metadata.timestamp);
92
+ const dateStr = date.toLocaleDateString() + " " + date.toLocaleTimeString();
93
+ console.log(` ${dateStr}`);
94
+ console.log(pc.gray(` Session: ${metadata.session.sessionId.substring(0, 8)}...`));
95
+ console.log(pc.gray(` File: ${file}`));
96
+ archives.push(metadata);
97
+ } catch {
98
+ }
99
+ }
100
+ if (files.length > 10) {
101
+ console.log(pc.gray(`
102
+ ... and ${files.length - 10} more`));
103
+ }
104
+ return archives;
105
+ }
106
+
107
+ export {
108
+ archiveCommand,
109
+ listArchives
110
+ };
@@ -0,0 +1,366 @@
1
+ import {
2
+ paths
3
+ } from "./chunk-A7YXSI66.js";
4
+
5
+ // src/core/process.ts
6
+ import { execSync, spawn } from "child_process";
7
+ import { platform } from "os";
8
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
9
+ import { join } from "path";
10
+ var ProcessManager = class {
11
+ platform = platform();
12
+ /**
13
+ * Find all Claude Code processes
14
+ */
15
+ findClaudeProcesses() {
16
+ try {
17
+ switch (this.platform) {
18
+ case "darwin":
19
+ case "linux":
20
+ return this.findUnixProcesses();
21
+ case "win32":
22
+ return this.findWindowsProcesses();
23
+ default:
24
+ throw new Error(`Unsupported platform: ${this.platform}`);
25
+ }
26
+ } catch (error) {
27
+ console.error(`Failed to find processes: ${error}`);
28
+ return [];
29
+ }
30
+ }
31
+ /**
32
+ * Find processes on Unix-like systems
33
+ */
34
+ findUnixProcesses() {
35
+ try {
36
+ const output = execSync("ps aux", { encoding: "utf-8" });
37
+ const lines = output.split("\n");
38
+ const processes = [];
39
+ for (const line of lines) {
40
+ if (line.includes("claude") && !line.includes("cs switch")) {
41
+ const parts = line.split(/\s+/);
42
+ if (parts.length >= 11 && parts[1]) {
43
+ const pid = parseInt(parts[1], 10);
44
+ const command = parts[10] || "";
45
+ const args = parts.slice(11);
46
+ if (command && (command.includes("claude") || args.some((arg) => arg && arg.includes("claude")))) {
47
+ processes.push({ pid, command, args });
48
+ }
49
+ }
50
+ }
51
+ }
52
+ return processes;
53
+ } catch (error) {
54
+ console.error(`Unix process detection failed: ${error}`);
55
+ return [];
56
+ }
57
+ }
58
+ /**
59
+ * Find processes on Windows using PowerShell (wmic is deprecated)
60
+ */
61
+ findWindowsProcesses() {
62
+ try {
63
+ const output = execSync(
64
+ `powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*claude*' } | Select-Object ProcessId,CommandLine | ConvertTo-Csv -NoTypeInformation"`,
65
+ { encoding: "utf-8" }
66
+ );
67
+ const lines = output.split("\r\n").filter((line) => line.trim());
68
+ const processes = [];
69
+ for (let i = 1; i < lines.length; i++) {
70
+ const line = lines[i];
71
+ if (!line) continue;
72
+ const match = line.match(/^"?(\d+)"?,"?(.*?)"?$/);
73
+ if (match) {
74
+ const pid = parseInt(match[1], 10);
75
+ const commandLine = match[2] || "";
76
+ if (commandLine.includes("claude") && !commandLine.includes("cs switch")) {
77
+ processes.push({
78
+ pid,
79
+ command: "claude",
80
+ args: commandLine.split(" ").slice(1)
81
+ });
82
+ }
83
+ }
84
+ }
85
+ return processes;
86
+ } catch (error) {
87
+ console.error(`Windows process detection failed: ${error}`);
88
+ return [];
89
+ }
90
+ }
91
+ /**
92
+ * Kill a process by PID
93
+ */
94
+ killProcess(pid, force = false) {
95
+ try {
96
+ if (this.platform === "win32") {
97
+ const flag = force ? "/F" : "";
98
+ execSync(`taskkill /PID ${pid} ${flag}`, { stdio: "ignore" });
99
+ } else {
100
+ const signal = force ? "SIGKILL" : "SIGTERM";
101
+ process.kill(pid, signal);
102
+ }
103
+ return true;
104
+ } catch (error) {
105
+ return false;
106
+ }
107
+ }
108
+ /**
109
+ * Kill all Claude processes (with timeout for graceful shutdown)
110
+ */
111
+ async killAllClaudeProcesses(timeout = 5e3) {
112
+ const processes = this.findClaudeProcesses();
113
+ if (processes.length === 0) {
114
+ return;
115
+ }
116
+ console.log(`Found ${processes.length} Claude process(es) to terminate`);
117
+ for (const proc of processes) {
118
+ this.killProcess(proc.pid, false);
119
+ }
120
+ await new Promise((resolve) => setTimeout(resolve, Math.min(timeout, 1e3)));
121
+ const remaining = this.findClaudeProcesses();
122
+ for (const proc of remaining) {
123
+ console.log(`Force killing process ${proc.pid}`);
124
+ this.killProcess(proc.pid, true);
125
+ }
126
+ }
127
+ /**
128
+ * Spawn Claude with specific arguments
129
+ */
130
+ spawnClaude(args, options) {
131
+ const claudeCmd = this.findClaudeExecutable();
132
+ if (!claudeCmd) {
133
+ throw new Error("Claude CLI not found. Please ensure it is installed and in your PATH.");
134
+ }
135
+ if (this.platform === "darwin") {
136
+ const cwd = options?.cwd || process.cwd();
137
+ const claudeCommand = `cd "${cwd}" && ${claudeCmd} ${args.join(" ")}`;
138
+ const script = `tell app "Terminal" to do script "${claudeCommand.replace(/"/g, '\\"')}"`;
139
+ const terminalProcess = spawn("osascript", ["-e", script], {
140
+ detached: true,
141
+ stdio: "ignore"
142
+ });
143
+ terminalProcess.unref();
144
+ return terminalProcess.pid || 0;
145
+ }
146
+ if (this.platform === "linux") {
147
+ const cwd = options?.cwd || process.cwd();
148
+ const claudeCommand = `${claudeCmd} ${args.join(" ")}`;
149
+ const terminals = ["gnome-terminal", "xterm", "konsole", "xfce4-terminal"];
150
+ for (const term of terminals) {
151
+ try {
152
+ const termProcess = spawn(term, ["--", "bash", "-c", `cd "${cwd}" && ${claudeCommand}`], {
153
+ detached: true,
154
+ stdio: "ignore"
155
+ });
156
+ termProcess.unref();
157
+ return termProcess.pid || 0;
158
+ } catch {
159
+ }
160
+ }
161
+ }
162
+ if (this.platform === "win32") {
163
+ const cwd = options?.cwd || process.cwd();
164
+ const gitBash = this.findGitBash();
165
+ if (gitBash) {
166
+ const claudeCommand = `cd "${cwd.replace(/\\/g, "/")}" && ${claudeCmd} ${args.join(" ")}`;
167
+ const cmdProcess = spawn("cmd.exe", ["/c", "start", "", gitBash, "-c", claudeCommand], {
168
+ detached: true,
169
+ stdio: "ignore",
170
+ windowsHide: false
171
+ });
172
+ cmdProcess.unref();
173
+ return cmdProcess.pid || 0;
174
+ } else {
175
+ const claudeCommand = `cd /d "${cwd}" && ${claudeCmd} ${args.join(" ")}`;
176
+ const cmdProcess = spawn("cmd.exe", ["/c", "start", "cmd.exe", "/k", claudeCommand], {
177
+ detached: true,
178
+ stdio: "ignore",
179
+ windowsHide: false
180
+ });
181
+ cmdProcess.unref();
182
+ return cmdProcess.pid || 0;
183
+ }
184
+ }
185
+ const claudeProcess = spawn(claudeCmd, args, {
186
+ detached: true,
187
+ stdio: "ignore",
188
+ ...options
189
+ });
190
+ claudeProcess.unref();
191
+ return claudeProcess.pid || 0;
192
+ }
193
+ /**
194
+ * Spawn a shell script in a new terminal window
195
+ */
196
+ spawnTerminalScript(script, cwd) {
197
+ const scriptFile = join(paths.baseDir, "launch.sh");
198
+ writeFileSync(scriptFile, script, { mode: 493 });
199
+ if (this.platform === "darwin") {
200
+ const escapedPath = scriptFile.replace(/"/g, '\\"');
201
+ const appleScript = `tell app "Terminal" to do script "bash \\"${escapedPath}\\""`;
202
+ const proc2 = spawn("osascript", ["-e", appleScript], {
203
+ detached: true,
204
+ stdio: "ignore"
205
+ });
206
+ proc2.unref();
207
+ return proc2.pid || 0;
208
+ }
209
+ if (this.platform === "linux") {
210
+ const terminals = ["gnome-terminal", "xterm", "konsole", "xfce4-terminal"];
211
+ for (const term of terminals) {
212
+ try {
213
+ const proc2 = spawn(term, ["--", "bash", scriptFile], {
214
+ detached: true,
215
+ stdio: "ignore"
216
+ });
217
+ proc2.unref();
218
+ return proc2.pid || 0;
219
+ } catch {
220
+ }
221
+ }
222
+ }
223
+ if (this.platform === "win32") {
224
+ const gitBash = this.findGitBash();
225
+ const bashCmd = gitBash || "bash";
226
+ const scriptPath = scriptFile.replace(/\\/g, "/");
227
+ const proc2 = spawn("cmd.exe", ["/c", "start", "", bashCmd, scriptPath], {
228
+ detached: true,
229
+ stdio: "ignore",
230
+ windowsHide: false
231
+ });
232
+ proc2.unref();
233
+ return proc2.pid || 0;
234
+ }
235
+ const proc = spawn("bash", [scriptFile], {
236
+ detached: true,
237
+ stdio: "ignore"
238
+ });
239
+ proc.unref();
240
+ return proc.pid || 0;
241
+ }
242
+ /**
243
+ * Get the Claude executable path (public accessor)
244
+ */
245
+ getClaudeExecutable() {
246
+ return this.findClaudeExecutable();
247
+ }
248
+ /**
249
+ * Find Git Bash executable on Windows
250
+ */
251
+ findGitBash() {
252
+ if (this.platform !== "win32") return null;
253
+ const candidates = [
254
+ join(process.env.PROGRAMFILES || "C:\\Program Files", "Git", "bin", "bash.exe"),
255
+ join(process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)", "Git", "bin", "bash.exe"),
256
+ join(process.env.LOCALAPPDATA || "", "Programs", "Git", "bin", "bash.exe"),
257
+ "C:\\Program Files\\Git\\bin\\bash.exe"
258
+ ];
259
+ for (const candidate of candidates) {
260
+ if (candidate && existsSync(candidate)) {
261
+ return candidate;
262
+ }
263
+ }
264
+ try {
265
+ const output = execSync("where bash", { encoding: "utf-8" }).trim();
266
+ const firstLine = output.split("\n")[0]?.trim();
267
+ if (firstLine && firstLine.toLowerCase().includes("git")) {
268
+ return firstLine;
269
+ }
270
+ } catch {
271
+ }
272
+ return null;
273
+ }
274
+ /**
275
+ * Find Claude executable path
276
+ */
277
+ findClaudeExecutable() {
278
+ try {
279
+ if (this.platform === "win32") {
280
+ const output = execSync("where claude", { encoding: "utf-8" }).trim();
281
+ const firstLine = output.split("\n")[0];
282
+ return firstLine || null;
283
+ } else {
284
+ const output = execSync("which claude", { encoding: "utf-8" }).trim();
285
+ return output || null;
286
+ }
287
+ } catch {
288
+ const commonPaths = this.platform === "win32" ? [
289
+ join(process.env.LOCALAPPDATA || "", "Programs", "Claude Code", "claude.exe"),
290
+ join(process.env.PROGRAMFILES || "C:\\Program Files", "Claude Code", "claude.exe"),
291
+ join(process.env.PROGRAMFILES || "C:\\Program Files", "Claude", "claude.exe"),
292
+ "C:\\Program Files\\Claude Code\\claude.exe",
293
+ "C:\\Program Files\\Claude\\claude.exe"
294
+ ] : [
295
+ "/usr/local/bin/claude",
296
+ "/usr/bin/claude",
297
+ "/opt/homebrew/bin/claude"
298
+ ];
299
+ for (const candidatePath of commonPaths) {
300
+ if (candidatePath && existsSync(candidatePath)) {
301
+ return candidatePath;
302
+ }
303
+ }
304
+ return null;
305
+ }
306
+ }
307
+ /**
308
+ * Write PID file for tracking
309
+ */
310
+ writePidFile(domainName, pid) {
311
+ const pidFile = join(paths.baseDir, `${domainName}.pid`);
312
+ writeFileSync(pidFile, pid.toString(), "utf-8");
313
+ }
314
+ /**
315
+ * Read PID file
316
+ */
317
+ readPidFile(domainName) {
318
+ const pidFile = join(paths.baseDir, `${domainName}.pid`);
319
+ if (!existsSync(pidFile)) {
320
+ return null;
321
+ }
322
+ try {
323
+ const content = readFileSync(pidFile, "utf-8");
324
+ return parseInt(content, 10);
325
+ } catch {
326
+ return null;
327
+ }
328
+ }
329
+ /**
330
+ * Delete PID file
331
+ */
332
+ deletePidFile(domainName) {
333
+ const pidFile = join(paths.baseDir, `${domainName}.pid`);
334
+ if (existsSync(pidFile)) {
335
+ unlinkSync(pidFile);
336
+ }
337
+ }
338
+ /**
339
+ * Check if a process is still running
340
+ */
341
+ isProcessRunning(pid) {
342
+ try {
343
+ if (this.platform === "win32") {
344
+ const output = execSync(`tasklist /FI "PID eq ${pid}"`, { encoding: "utf-8" });
345
+ return output.includes(pid.toString());
346
+ } else {
347
+ process.kill(pid, 0);
348
+ return true;
349
+ }
350
+ } catch {
351
+ return false;
352
+ }
353
+ }
354
+ /**
355
+ * Verify Claude is installed
356
+ */
357
+ verifyClaudeInstalled() {
358
+ return this.findClaudeExecutable() !== null;
359
+ }
360
+ };
361
+ var processManager = new ProcessManager();
362
+
363
+ export {
364
+ ProcessManager,
365
+ processManager
366
+ };