shiva-code 0.5.0 → 0.5.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/dist/index.js CHANGED
@@ -49,7 +49,7 @@ import {
49
49
  getPackageLaunchConfig,
50
50
  getPackageStats,
51
51
  removeProjectFromPackage
52
- } from "./chunk-5RCSFT5F.js";
52
+ } from "./chunk-LBTCSQAX.js";
53
53
  import {
54
54
  encodeProjectPath,
55
55
  findProject,
@@ -67,8 +67,12 @@ import {
67
67
  isSessionActive,
68
68
  isSessionCorrupted,
69
69
  isSessionCorruptedQuick,
70
+ isValidProjectPath,
71
+ isValidSessionId,
72
+ maskSecret,
73
+ sanitizeForLog,
70
74
  saveRecoveredContext
71
- } from "./chunk-IDM6KY2R.js";
75
+ } from "./chunk-H5OFO4VS.js";
72
76
  import {
73
77
  cache
74
78
  } from "./chunk-HIQO2DBA.js";
@@ -310,9 +314,9 @@ var ApiClient = class {
310
314
  return this.request("/projects/stats");
311
315
  }
312
316
  // Find project by path
313
- async findProjectByPath(path15) {
317
+ async findProjectByPath(path16) {
314
318
  const projects = await this.getProjects();
315
- return projects.find((p) => p.path === path15) || null;
319
+ return projects.find((p) => p.path === path16) || null;
316
320
  }
317
321
  // ============================================
318
322
  // Secrets Vault Endpoints
@@ -889,21 +893,21 @@ function generateBackupCodes(count = 10) {
889
893
  return codes;
890
894
  }
891
895
  function generateDeviceFingerprint() {
892
- const os7 = __require("os");
896
+ const os8 = __require("os");
893
897
  const components = [
894
- os7.hostname(),
895
- os7.platform(),
896
- os7.arch(),
897
- os7.cpus()[0]?.model || "unknown",
898
- os7.userInfo().username
898
+ os8.hostname(),
899
+ os8.platform(),
900
+ os8.arch(),
901
+ os8.cpus()[0]?.model || "unknown",
902
+ os8.userInfo().username
899
903
  ];
900
904
  const fingerprint = crypto.createHash("sha256").update(components.join("|")).digest("hex").slice(0, 32);
901
905
  return fingerprint;
902
906
  }
903
907
  function getDeviceName() {
904
- const os7 = __require("os");
905
- const hostname = os7.hostname();
906
- const platform = os7.platform();
908
+ const os8 = __require("os");
909
+ const hostname = os8.hostname();
910
+ const platform = os8.platform();
907
911
  const platformNames = {
908
912
  darwin: "macOS",
909
913
  linux: "Linux",
@@ -2401,9 +2405,9 @@ var Errors = {
2401
2405
  "Bitte sp\xE4ter erneut versuchen"
2402
2406
  ),
2403
2407
  // File/Path errors
2404
- PATH_NOT_FOUND: (path15) => new ShivaError(
2408
+ PATH_NOT_FOUND: (path16) => new ShivaError(
2405
2409
  "PATH_404",
2406
- path15 ? `Pfad nicht gefunden: ${path15}` : "Pfad nicht gefunden",
2410
+ path16 ? `Pfad nicht gefunden: ${path16}` : "Pfad nicht gefunden",
2407
2411
  "Pfad \xFCberpr\xFCfen und erneut versuchen"
2408
2412
  ),
2409
2413
  FILE_NOT_FOUND: (file) => new ShivaError(
@@ -2411,9 +2415,9 @@ var Errors = {
2411
2415
  file ? `Datei nicht gefunden: ${file}` : "Datei nicht gefunden",
2412
2416
  void 0
2413
2417
  ),
2414
- PERMISSION_DENIED: (path15) => new ShivaError(
2418
+ PERMISSION_DENIED: (path16) => new ShivaError(
2415
2419
  "PERMISSION_DENIED",
2416
- path15 ? `Keine Berechtigung: ${path15}` : "Keine Berechtigung",
2420
+ path16 ? `Keine Berechtigung: ${path16}` : "Keine Berechtigung",
2417
2421
  "Berechtigungen pr\xFCfen oder mit sudo ausf\xFChren"
2418
2422
  ),
2419
2423
  // Package errors
@@ -4401,15 +4405,17 @@ projectCommand.command("status").description("Projekt-Status anzeigen").action(a
4401
4405
  // src/commands/session/start.ts
4402
4406
  import { Command as Command10 } from "commander";
4403
4407
  import * as fs from "fs";
4404
- import * as path3 from "path";
4408
+ import * as path4 from "path";
4405
4409
  import ora6 from "ora";
4406
4410
  import { spawnSync as spawnSync4 } from "child_process";
4407
4411
 
4408
4412
  // src/services/infrastructure/terminal.ts
4409
- import { spawn as spawn2, execSync as execSync2, spawnSync as spawnSync2 } from "child_process";
4413
+ import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
4414
+ import * as path3 from "path";
4415
+ import * as os3 from "os";
4410
4416
 
4411
4417
  // src/services/infrastructure/docker.ts
4412
- import { execSync, spawn } from "child_process";
4418
+ import { spawn, spawnSync } from "child_process";
4413
4419
  import * as path2 from "path";
4414
4420
  import * as os2 from "os";
4415
4421
  var DockerService = class {
@@ -4422,33 +4428,46 @@ var DockerService = class {
4422
4428
  }
4423
4429
  /**
4424
4430
  * Get Docker runtime information
4431
+ * SECURITY: Uses spawnSync with array args
4425
4432
  */
4426
4433
  getDockerInfo() {
4427
4434
  if (this.cachedInfo) {
4428
4435
  return this.cachedInfo;
4429
4436
  }
4430
4437
  try {
4431
- const version = execSync("docker --version", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
4432
- const socket = this.findDockerSocket("docker");
4433
- this.cachedInfo = {
4434
- available: true,
4435
- version: version.replace("Docker version ", "").split(",")[0],
4436
- runtime: "docker",
4437
- socket
4438
- };
4439
- return this.cachedInfo;
4438
+ const result = spawnSync("docker", ["--version"], {
4439
+ encoding: "utf8",
4440
+ stdio: ["pipe", "pipe", "pipe"]
4441
+ });
4442
+ if (result.status === 0 && result.stdout) {
4443
+ const version = result.stdout.trim();
4444
+ const socket = this.findDockerSocket("docker");
4445
+ this.cachedInfo = {
4446
+ available: true,
4447
+ version: version.replace("Docker version ", "").split(",")[0],
4448
+ runtime: "docker",
4449
+ socket
4450
+ };
4451
+ return this.cachedInfo;
4452
+ }
4440
4453
  } catch {
4441
4454
  }
4442
4455
  try {
4443
- const version = execSync("podman --version", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
4444
- const socket = this.findDockerSocket("podman");
4445
- this.cachedInfo = {
4446
- available: true,
4447
- version: version.replace("podman version ", ""),
4448
- runtime: "podman",
4449
- socket
4450
- };
4451
- return this.cachedInfo;
4456
+ const result = spawnSync("podman", ["--version"], {
4457
+ encoding: "utf8",
4458
+ stdio: ["pipe", "pipe", "pipe"]
4459
+ });
4460
+ if (result.status === 0 && result.stdout) {
4461
+ const version = result.stdout.trim();
4462
+ const socket = this.findDockerSocket("podman");
4463
+ this.cachedInfo = {
4464
+ available: true,
4465
+ version: version.replace("podman version ", ""),
4466
+ runtime: "podman",
4467
+ socket
4468
+ };
4469
+ return this.cachedInfo;
4470
+ }
4452
4471
  } catch {
4453
4472
  }
4454
4473
  this.cachedInfo = {
@@ -4492,8 +4511,12 @@ var DockerService = class {
4492
4511
  // ============================================
4493
4512
  /**
4494
4513
  * Pull a Docker image
4514
+ * SECURITY: Validates image name before use
4495
4515
  */
4496
4516
  async pullImage(image) {
4517
+ if (!this.isValidImage(image)) {
4518
+ throw new Error(`Invalid image name: ${sanitizeForLog(image)}`);
4519
+ }
4497
4520
  const cmd = this.getCommand();
4498
4521
  return new Promise((resolve13, reject) => {
4499
4522
  const pull = spawn(cmd, ["pull", image], { stdio: "inherit" });
@@ -4501,7 +4524,7 @@ var DockerService = class {
4501
4524
  if (code === 0) {
4502
4525
  resolve13();
4503
4526
  } else {
4504
- reject(new Error(`Failed to pull image: ${image}`));
4527
+ reject(new Error(`Failed to pull image: ${sanitizeForLog(image)}`));
4505
4528
  }
4506
4529
  });
4507
4530
  pull.on("error", (err) => {
@@ -4509,14 +4532,32 @@ var DockerService = class {
4509
4532
  });
4510
4533
  });
4511
4534
  }
4535
+ /**
4536
+ * Validate an image name
4537
+ */
4538
+ isValidImage(image) {
4539
+ if (!image || typeof image !== "string") {
4540
+ return false;
4541
+ }
4542
+ if (/[;&|`$(){}[\]<>\\!#*?"'\n\r\t]/.test(image)) {
4543
+ return false;
4544
+ }
4545
+ return image.length > 0 && image.length <= 256;
4546
+ }
4512
4547
  /**
4513
4548
  * Check if an image exists locally
4549
+ * SECURITY: Uses spawnSync with array args
4514
4550
  */
4515
4551
  imageExists(image) {
4552
+ if (!this.isValidImage(image)) {
4553
+ return false;
4554
+ }
4516
4555
  const cmd = this.getCommand();
4517
4556
  try {
4518
- execSync(`${cmd} image inspect ${image}`, { stdio: "ignore" });
4519
- return true;
4557
+ const result = spawnSync(cmd, ["image", "inspect", image], {
4558
+ stdio: "pipe"
4559
+ });
4560
+ return result.status === 0;
4520
4561
  } catch {
4521
4562
  return false;
4522
4563
  }
@@ -4530,14 +4571,23 @@ var DockerService = class {
4530
4571
  }
4531
4572
  /**
4532
4573
  * List local images
4574
+ * SECURITY: Uses spawnSync with array args
4533
4575
  */
4534
4576
  listImages() {
4535
4577
  const cmd = this.getCommand();
4536
4578
  try {
4537
- const output = execSync(`${cmd} images --format "{{.Repository}} {{.Tag}} {{.ID}} {{.Size}}"`, {
4538
- encoding: "utf8"
4579
+ const result = spawnSync(cmd, [
4580
+ "images",
4581
+ "--format",
4582
+ "{{.Repository}} {{.Tag}} {{.ID}} {{.Size}}"
4583
+ ], {
4584
+ encoding: "utf8",
4585
+ stdio: ["pipe", "pipe", "pipe"]
4539
4586
  });
4540
- return output.trim().split("\n").filter((line) => line.includes("shiva") || line.includes("claude")).map((line) => {
4587
+ if (result.status !== 0 || !result.stdout) {
4588
+ return [];
4589
+ }
4590
+ return result.stdout.trim().split("\n").filter((line) => line.includes("shiva") || line.includes("claude")).map((line) => {
4541
4591
  const [repository, tag, id, size] = line.split(" ");
4542
4592
  return { repository, tag, id, size };
4543
4593
  });
@@ -4548,24 +4598,55 @@ var DockerService = class {
4548
4598
  // ============================================
4549
4599
  // Container Lifecycle
4550
4600
  // ============================================
4601
+ /**
4602
+ * Validate container ID format
4603
+ */
4604
+ isValidContainerId(id) {
4605
+ return /^[a-f0-9]{12,64}$/i.test(id);
4606
+ }
4607
+ /**
4608
+ * Validate container name
4609
+ */
4610
+ isValidContainerNameInternal(name) {
4611
+ return /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name) && name.length <= 128;
4612
+ }
4551
4613
  /**
4552
4614
  * Create a new container
4615
+ * SECURITY: Uses spawnSync with array args, validates all inputs
4553
4616
  */
4554
4617
  async createContainer(config2) {
4555
4618
  const cmd = this.getCommand();
4556
4619
  const args = ["create"];
4557
4620
  if (config2.name) {
4621
+ if (!this.isValidContainerNameInternal(config2.name)) {
4622
+ throw new Error(`Invalid container name: ${sanitizeForLog(config2.name)}`);
4623
+ }
4558
4624
  args.push("--name", config2.name);
4559
4625
  }
4626
+ if (!isValidProjectPath(config2.workdir)) {
4627
+ throw new Error(`Invalid workdir: ${sanitizeForLog(config2.workdir)}`);
4628
+ }
4560
4629
  args.push("--workdir", config2.workdir);
4561
4630
  for (const mount of config2.mounts) {
4631
+ if (!isValidProjectPath(mount.host)) {
4632
+ log.warn(`Skipping invalid mount path: ${sanitizeForLog(mount.host)}`);
4633
+ continue;
4634
+ }
4562
4635
  args.push("-v", `${mount.host}:${mount.container}:${mount.mode}`);
4563
4636
  }
4564
4637
  for (const [key, value] of Object.entries(config2.env)) {
4638
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) {
4639
+ log.warn(`Skipping invalid env var name: ${sanitizeForLog(key)}`);
4640
+ continue;
4641
+ }
4565
4642
  args.push("-e", `${key}=${value}`);
4566
4643
  }
4567
4644
  if (config2.ports) {
4568
4645
  for (const [host, container] of Object.entries(config2.ports)) {
4646
+ if (!/^\d+$/.test(String(host)) || !/^\d+$/.test(String(container))) {
4647
+ log.warn(`Skipping invalid port mapping: ${host}:${container}`);
4648
+ continue;
4649
+ }
4569
4650
  args.push("-p", `${host}:${container}`);
4570
4651
  }
4571
4652
  }
@@ -4575,55 +4656,103 @@ var DockerService = class {
4575
4656
  if (config2.autoRemove) {
4576
4657
  args.push("--rm");
4577
4658
  }
4659
+ if (!this.isValidImage(config2.image)) {
4660
+ throw new Error(`Invalid image: ${sanitizeForLog(config2.image)}`);
4661
+ }
4578
4662
  args.push(config2.image);
4579
4663
  try {
4580
- const containerId = execSync(`${cmd} ${args.join(" ")}`, { encoding: "utf8" }).trim();
4581
- return containerId;
4664
+ const result = spawnSync(cmd, args, {
4665
+ encoding: "utf8",
4666
+ stdio: ["pipe", "pipe", "pipe"]
4667
+ });
4668
+ if (result.status !== 0) {
4669
+ throw new Error(`Failed to create container: ${result.stderr || "Unknown error"}`);
4670
+ }
4671
+ return result.stdout.trim();
4582
4672
  } catch (error) {
4583
4673
  throw new Error(`Failed to create container: ${error instanceof Error ? error.message : "Unknown error"}`);
4584
4674
  }
4585
4675
  }
4586
4676
  /**
4587
4677
  * Start a container
4678
+ * SECURITY: Validates container ID format
4588
4679
  */
4589
4680
  async startContainer(containerId) {
4681
+ if (!this.isValidContainerId(containerId) && !this.isValidContainerNameInternal(containerId)) {
4682
+ throw new Error(`Invalid container ID: ${sanitizeForLog(containerId)}`);
4683
+ }
4590
4684
  const cmd = this.getCommand();
4591
4685
  try {
4592
- execSync(`${cmd} start ${containerId}`, { stdio: "ignore" });
4686
+ const result = spawnSync(cmd, ["start", containerId], {
4687
+ stdio: "pipe"
4688
+ });
4689
+ if (result.status !== 0) {
4690
+ throw new Error(`Failed to start container`);
4691
+ }
4593
4692
  } catch (error) {
4594
4693
  throw new Error(`Failed to start container: ${error instanceof Error ? error.message : "Unknown error"}`);
4595
4694
  }
4596
4695
  }
4597
4696
  /**
4598
4697
  * Stop a container
4698
+ * SECURITY: Validates container ID format
4599
4699
  */
4600
4700
  async stopContainer(containerId) {
4701
+ if (!this.isValidContainerId(containerId) && !this.isValidContainerNameInternal(containerId)) {
4702
+ throw new Error(`Invalid container ID: ${sanitizeForLog(containerId)}`);
4703
+ }
4601
4704
  const cmd = this.getCommand();
4602
4705
  try {
4603
- execSync(`${cmd} stop ${containerId}`, { stdio: "ignore" });
4706
+ const result = spawnSync(cmd, ["stop", containerId], {
4707
+ stdio: "pipe"
4708
+ });
4709
+ if (result.status !== 0) {
4710
+ throw new Error(`Failed to stop container`);
4711
+ }
4604
4712
  } catch (error) {
4605
4713
  throw new Error(`Failed to stop container: ${error instanceof Error ? error.message : "Unknown error"}`);
4606
4714
  }
4607
4715
  }
4608
4716
  /**
4609
4717
  * Remove a container
4718
+ * SECURITY: Validates container ID format
4610
4719
  */
4611
4720
  async removeContainer(containerId) {
4721
+ if (!this.isValidContainerId(containerId) && !this.isValidContainerNameInternal(containerId)) {
4722
+ throw new Error(`Invalid container ID: ${sanitizeForLog(containerId)}`);
4723
+ }
4612
4724
  const cmd = this.getCommand();
4613
4725
  try {
4614
- execSync(`${cmd} rm -f ${containerId}`, { stdio: "ignore" });
4726
+ const result = spawnSync(cmd, ["rm", "-f", containerId], {
4727
+ stdio: "pipe"
4728
+ });
4729
+ if (result.status !== 0) {
4730
+ throw new Error(`Failed to remove container`);
4731
+ }
4615
4732
  } catch (error) {
4616
4733
  throw new Error(`Failed to remove container: ${error instanceof Error ? error.message : "Unknown error"}`);
4617
4734
  }
4618
4735
  }
4619
4736
  /**
4620
4737
  * Get container logs
4738
+ * SECURITY: Validates container ID, uses array args
4621
4739
  */
4622
4740
  getContainerLogs(containerId, tail) {
4741
+ if (!this.isValidContainerId(containerId) && !this.isValidContainerNameInternal(containerId)) {
4742
+ return "";
4743
+ }
4623
4744
  const cmd = this.getCommand();
4624
- const tailArg = tail ? `--tail ${tail}` : "";
4745
+ const args = ["logs"];
4746
+ if (tail && tail > 0) {
4747
+ args.push("--tail", String(tail));
4748
+ }
4749
+ args.push(containerId);
4625
4750
  try {
4626
- return execSync(`${cmd} logs ${tailArg} ${containerId}`, { encoding: "utf8" });
4751
+ const result = spawnSync(cmd, args, {
4752
+ encoding: "utf8",
4753
+ stdio: ["pipe", "pipe", "pipe"]
4754
+ });
4755
+ return result.stdout || "";
4627
4756
  } catch {
4628
4757
  return "";
4629
4758
  }
@@ -4633,16 +4762,27 @@ var DockerService = class {
4633
4762
  // ============================================
4634
4763
  /**
4635
4764
  * Run Claude Code in a container
4765
+ * SECURITY: Validates all inputs, uses spawn with array args
4636
4766
  */
4637
4767
  async runClaudeInContainer(launch, dockerSettings) {
4768
+ if (!isValidProjectPath(launch.projectPath)) {
4769
+ throw new Error(`Invalid project path: ${sanitizeForLog(launch.projectPath)}`);
4770
+ }
4771
+ if (launch.sessionId && !isValidSessionId(launch.sessionId)) {
4772
+ throw new Error(`Invalid session ID: ${sanitizeForLog(launch.sessionId)}`);
4773
+ }
4638
4774
  const cmd = this.getCommand();
4639
4775
  const settings = dockerSettings || settingsSync.getDockerSettings();
4640
4776
  const image = settings.defaultImage || this.getDefaultImage();
4777
+ if (!this.isValidImage(image)) {
4778
+ throw new Error(`Invalid image: ${sanitizeForLog(image)}`);
4779
+ }
4641
4780
  if (!this.imageExists(image)) {
4642
- log.info(`Pulling Docker image: ${image}...`);
4781
+ log.info(`Pulling Docker image: ${sanitizeForLog(image)}...`);
4643
4782
  await this.pullImage(image);
4644
4783
  }
4645
- const containerName = `shiva-${launch.projectName.replace(/[^a-zA-Z0-9-]/g, "-")}-${Date.now()}`;
4784
+ const safeName = launch.projectName.replace(/[^a-zA-Z0-9-]/g, "-").substring(0, 50);
4785
+ const containerName = `shiva-${safeName}-${Date.now()}`;
4646
4786
  const claudeArgs = [];
4647
4787
  if (launch.sessionId && !launch.newSession) {
4648
4788
  claudeArgs.push("--resume", launch.sessionId);
@@ -4671,13 +4811,19 @@ var DockerService = class {
4671
4811
  `${path2.join(os2.homedir(), ".config", "shiva-code")}:/root/.config/shiva-code:rw`
4672
4812
  ];
4673
4813
  for (const [host, container] of Object.entries(settings.volumeMounts || {})) {
4674
- args.push("-v", `${host}:${container}:rw`);
4814
+ if (isValidProjectPath(host)) {
4815
+ args.push("-v", `${host}:${container}:rw`);
4816
+ }
4675
4817
  }
4676
4818
  for (const [key, value] of Object.entries(settings.environment || {})) {
4677
- args.push("-e", `${key}=${value}`);
4819
+ if (/^[A-Z_][A-Z0-9_]*$/i.test(key)) {
4820
+ args.push("-e", `${key}=${value}`);
4821
+ }
4678
4822
  }
4679
4823
  for (const [key, value] of Object.entries(secrets)) {
4680
- args.push("-e", `${key}=${value}`);
4824
+ if (/^[A-Z_][A-Z0-9_]*$/i.test(key)) {
4825
+ args.push("-e", `${key}=${value}`);
4826
+ }
4681
4827
  }
4682
4828
  args.push(image);
4683
4829
  args.push("claude", ...claudeArgs);
@@ -4695,15 +4841,26 @@ var DockerService = class {
4695
4841
  }
4696
4842
  /**
4697
4843
  * List all SHIVA containers
4844
+ * SECURITY: Uses spawnSync with array args
4698
4845
  */
4699
4846
  listShivaContainers() {
4700
4847
  const cmd = this.getCommand();
4701
4848
  try {
4702
- const output = execSync(
4703
- `${cmd} ps -a --filter "name=shiva-" --format "{{.ID}} {{.Names}} {{.Image}} {{.Status}} {{.CreatedAt}} {{.Ports}}"`,
4704
- { encoding: "utf8" }
4705
- );
4706
- return output.trim().split("\n").filter((line) => line.length > 0).map((line) => {
4849
+ const result = spawnSync(cmd, [
4850
+ "ps",
4851
+ "-a",
4852
+ "--filter",
4853
+ "name=shiva-",
4854
+ "--format",
4855
+ "{{.ID}} {{.Names}} {{.Image}} {{.Status}} {{.CreatedAt}} {{.Ports}}"
4856
+ ], {
4857
+ encoding: "utf8",
4858
+ stdio: ["pipe", "pipe", "pipe"]
4859
+ });
4860
+ if (result.status !== 0 || !result.stdout) {
4861
+ return [];
4862
+ }
4863
+ return result.stdout.trim().split("\n").filter((line) => line.length > 0).map((line) => {
4707
4864
  const [id, name, image, statusRaw, createdAt, ports] = line.split(" ");
4708
4865
  let status = "stopped";
4709
4866
  if (statusRaw.toLowerCase().includes("up")) {
@@ -4799,12 +4956,18 @@ var DockerService = class {
4799
4956
  * Set default Docker image
4800
4957
  */
4801
4958
  setDefaultImage(image) {
4959
+ if (!this.isValidImage(image)) {
4960
+ throw new Error(`Invalid image name: ${sanitizeForLog(image)}`);
4961
+ }
4802
4962
  settingsSync.setDockerImage(image);
4803
4963
  }
4804
4964
  /**
4805
4965
  * Add a volume mount
4806
4966
  */
4807
4967
  addVolumeMount(hostPath, containerPath) {
4968
+ if (!isValidProjectPath(hostPath)) {
4969
+ throw new Error(`Invalid host path: ${sanitizeForLog(hostPath)}`);
4970
+ }
4808
4971
  const settings = settingsSync.getDockerSettings();
4809
4972
  settingsSync.setDockerSettings({
4810
4973
  volumeMounts: {
@@ -4827,6 +4990,9 @@ var DockerService = class {
4827
4990
  * Add an environment variable
4828
4991
  */
4829
4992
  addEnvVar(key, value) {
4993
+ if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) {
4994
+ throw new Error(`Invalid environment variable name: ${sanitizeForLog(key)}`);
4995
+ }
4830
4996
  const settings = settingsSync.getDockerSettings();
4831
4997
  settingsSync.setDockerSettings({
4832
4998
  environment: {
@@ -4911,15 +5077,26 @@ var DockerService = class {
4911
5077
  }
4912
5078
  /**
4913
5079
  * Run Claude in container with project-specific settings
5080
+ * SECURITY: Validates all inputs, uses spawn with array args
4914
5081
  */
4915
5082
  async runClaudeInContainerWithProjectConfig(launch, projectPath) {
5083
+ if (!isValidProjectPath(launch.projectPath)) {
5084
+ throw new Error(`Invalid project path: ${sanitizeForLog(launch.projectPath)}`);
5085
+ }
5086
+ if (launch.sessionId && !isValidSessionId(launch.sessionId)) {
5087
+ throw new Error(`Invalid session ID: ${sanitizeForLog(launch.sessionId)}`);
5088
+ }
4916
5089
  const effectiveConfig = this.getEffectiveDockerConfig(projectPath);
4917
5090
  const cmd = this.getCommand();
5091
+ if (!this.isValidImage(effectiveConfig.image)) {
5092
+ throw new Error(`Invalid image: ${sanitizeForLog(effectiveConfig.image)}`);
5093
+ }
4918
5094
  if (!this.imageExists(effectiveConfig.image)) {
4919
- log.info(`Pulling Docker image: ${effectiveConfig.image}...`);
5095
+ log.info(`Pulling Docker image: ${sanitizeForLog(effectiveConfig.image)}...`);
4920
5096
  await this.pullImage(effectiveConfig.image);
4921
5097
  }
4922
- const containerName = `shiva-${launch.projectName.replace(/[^a-zA-Z0-9-]/g, "-")}-${Date.now()}`;
5098
+ const safeName = launch.projectName.replace(/[^a-zA-Z0-9-]/g, "-").substring(0, 50);
5099
+ const containerName = `shiva-${safeName}-${Date.now()}`;
4923
5100
  const claudeArgs = [];
4924
5101
  if (launch.sessionId && !launch.newSession) {
4925
5102
  claudeArgs.push("--resume", launch.sessionId);
@@ -4943,26 +5120,34 @@ var DockerService = class {
4943
5120
  } else if (effectiveConfig.network === "host") {
4944
5121
  args.push("--network", "host");
4945
5122
  }
4946
- if (effectiveConfig.resources.cpuLimit) {
5123
+ if (effectiveConfig.resources.cpuLimit && /^[\d.]+$/.test(effectiveConfig.resources.cpuLimit)) {
4947
5124
  args.push("--cpus", effectiveConfig.resources.cpuLimit);
4948
5125
  }
4949
- if (effectiveConfig.resources.memoryLimit) {
5126
+ if (effectiveConfig.resources.memoryLimit && /^[\d]+[kmgKMG]?$/.test(effectiveConfig.resources.memoryLimit)) {
4950
5127
  args.push("--memory", effectiveConfig.resources.memoryLimit);
4951
5128
  }
4952
5129
  for (const opt of effectiveConfig.securityOpts) {
4953
- args.push("--security-opt", opt);
5130
+ if (/^[a-z:=_-]+$/i.test(opt)) {
5131
+ args.push("--security-opt", opt);
5132
+ }
4954
5133
  }
4955
5134
  args.push("-v", `${launch.projectPath}:/workspace:rw`);
4956
5135
  args.push("-v", `${path2.join(os2.homedir(), ".claude")}:/root/.claude:rw`);
4957
5136
  args.push("-v", `${path2.join(os2.homedir(), ".config", "shiva-code")}:/root/.config/shiva-code:rw`);
4958
5137
  for (const mount of effectiveConfig.mounts) {
4959
- args.push("-v", `${mount.host}:${mount.container}:${mount.mode}`);
5138
+ if (isValidProjectPath(mount.host)) {
5139
+ args.push("-v", `${mount.host}:${mount.container}:${mount.mode}`);
5140
+ }
4960
5141
  }
4961
5142
  for (const [key, value] of Object.entries(effectiveConfig.environment)) {
4962
- args.push("-e", `${key}=${value}`);
5143
+ if (/^[A-Z_][A-Z0-9_]*$/i.test(key)) {
5144
+ args.push("-e", `${key}=${value}`);
5145
+ }
4963
5146
  }
4964
5147
  for (const [key, value] of Object.entries(secrets)) {
4965
- args.push("-e", `${key}=${value}`);
5148
+ if (/^[A-Z_][A-Z0-9_]*$/i.test(key)) {
5149
+ args.push("-e", `${key}=${value}`);
5150
+ }
4966
5151
  }
4967
5152
  args.push(effectiveConfig.image);
4968
5153
  args.push("claude", ...claudeArgs);
@@ -4977,8 +5162,11 @@ var dockerService = new DockerService();
4977
5162
  // src/services/infrastructure/terminal.ts
4978
5163
  function commandExists(command) {
4979
5164
  try {
4980
- execSync2(`which ${command}`, { stdio: "ignore" });
4981
- return true;
5165
+ if (!/^[a-zA-Z0-9_-]+$/.test(command)) {
5166
+ return false;
5167
+ }
5168
+ const result = spawnSync2("which", [command], { stdio: "pipe" });
5169
+ return result.status === 0;
4982
5170
  } catch {
4983
5171
  return false;
4984
5172
  }
@@ -5016,90 +5204,93 @@ function isValidTerminalType(value) {
5016
5204
  function isInsideTmux() {
5017
5205
  return !!process.env.TMUX;
5018
5206
  }
5019
- function buildClaudeCommand(project) {
5020
- const cdCommand = `cd "${project.projectPath}"`;
5021
- const launchArgs = getClaudeLaunchArgs();
5022
- const argsStr = launchArgs.length > 0 ? ` ${launchArgs.join(" ")}` : "";
5023
- if (project.newSession || !project.sessionId) {
5024
- return `${cdCommand} && claude${argsStr}`;
5207
+ function validateProject(project) {
5208
+ if (!isValidProjectPath(project.projectPath)) {
5209
+ return {
5210
+ valid: false,
5211
+ error: `Invalid project path: ${sanitizeForLog(project.projectPath)}`
5212
+ };
5025
5213
  }
5026
- return `${cdCommand} && claude --resume ${project.sessionId}${argsStr}`;
5214
+ if (project.sessionId && !isValidSessionId(project.sessionId)) {
5215
+ return {
5216
+ valid: false,
5217
+ error: `Invalid session ID format: ${sanitizeForLog(project.sessionId)}`
5218
+ };
5219
+ }
5220
+ return { valid: true };
5221
+ }
5222
+ function buildClaudeArgs(project) {
5223
+ const args = [];
5224
+ if (!project.newSession && project.sessionId) {
5225
+ args.push("--resume", project.sessionId);
5226
+ }
5227
+ const launchArgs = getClaudeLaunchArgs();
5228
+ args.push(...launchArgs);
5229
+ return args;
5230
+ }
5231
+ function sanitizeProjectName(name) {
5232
+ return name.replace(/[^a-zA-Z0-9_.-]/g, "-").substring(0, 50);
5027
5233
  }
5028
5234
  async function spawnInTmux(projects, sessionName) {
5029
5235
  const tmuxSession = sessionName || `shiva-${Date.now()}`;
5030
5236
  const insideTmux = isInsideTmux();
5031
- if (insideTmux) {
5032
- for (let i = 0; i < projects.length; i++) {
5033
- const project = projects[i];
5034
- const command = buildClaudeCommand(project);
5237
+ for (let i = 0; i < projects.length; i++) {
5238
+ const project = projects[i];
5239
+ const validation = validateProject(project);
5240
+ if (!validation.valid) {
5241
+ console.error(`Skipping invalid project: ${validation.error}`);
5242
+ continue;
5243
+ }
5244
+ const claudeArgs = buildClaudeArgs(project);
5245
+ const safeName = sanitizeProjectName(project.projectName);
5246
+ if (insideTmux) {
5247
+ spawnSync2("tmux", [
5248
+ "new-window",
5249
+ "-n",
5250
+ safeName,
5251
+ "-c",
5252
+ project.projectPath
5253
+ ], { stdio: "inherit" });
5254
+ const claudeCmd = ["claude", ...claudeArgs].join(" ");
5255
+ spawnSync2("tmux", [
5256
+ "send-keys",
5257
+ claudeCmd,
5258
+ "Enter"
5259
+ ], { stdio: "inherit" });
5260
+ } else {
5035
5261
  if (i === 0) {
5036
5262
  spawnSync2("tmux", [
5037
- "new-window",
5263
+ "new-session",
5264
+ "-d",
5265
+ "-s",
5266
+ tmuxSession,
5038
5267
  "-n",
5039
- project.projectName,
5268
+ safeName,
5040
5269
  "-c",
5041
5270
  project.projectPath
5042
5271
  ], { stdio: "inherit" });
5043
- spawnSync2("tmux", [
5044
- "send-keys",
5045
- command,
5046
- "Enter"
5047
- ], { stdio: "inherit" });
5048
5272
  } else {
5049
5273
  spawnSync2("tmux", [
5050
5274
  "new-window",
5275
+ "-t",
5276
+ tmuxSession,
5051
5277
  "-n",
5052
- project.projectName,
5278
+ safeName,
5053
5279
  "-c",
5054
5280
  project.projectPath
5055
5281
  ], { stdio: "inherit" });
5056
- spawnSync2("tmux", [
5057
- "send-keys",
5058
- command,
5059
- "Enter"
5060
- ], { stdio: "inherit" });
5061
5282
  }
5062
- }
5063
- } else {
5064
- const firstProject = projects[0];
5065
- const firstCommand = buildClaudeCommand(firstProject);
5066
- spawnSync2("tmux", [
5067
- "new-session",
5068
- "-d",
5069
- "-s",
5070
- tmuxSession,
5071
- "-n",
5072
- firstProject.projectName,
5073
- "-c",
5074
- firstProject.projectPath
5075
- ], { stdio: "inherit" });
5076
- spawnSync2("tmux", [
5077
- "send-keys",
5078
- "-t",
5079
- `${tmuxSession}:${firstProject.projectName}`,
5080
- firstCommand,
5081
- "Enter"
5082
- ], { stdio: "inherit" });
5083
- for (let i = 1; i < projects.length; i++) {
5084
- const project = projects[i];
5085
- const command = buildClaudeCommand(project);
5086
- spawnSync2("tmux", [
5087
- "new-window",
5088
- "-t",
5089
- tmuxSession,
5090
- "-n",
5091
- project.projectName,
5092
- "-c",
5093
- project.projectPath
5094
- ], { stdio: "inherit" });
5283
+ const claudeCmd = ["claude", ...claudeArgs].join(" ");
5095
5284
  spawnSync2("tmux", [
5096
5285
  "send-keys",
5097
5286
  "-t",
5098
- `${tmuxSession}:${project.projectName}`,
5099
- command,
5287
+ `${tmuxSession}:${safeName}`,
5288
+ claudeCmd,
5100
5289
  "Enter"
5101
5290
  ], { stdio: "inherit" });
5102
5291
  }
5292
+ }
5293
+ if (!insideTmux) {
5103
5294
  spawn2("tmux", ["attach-session", "-t", tmuxSession], {
5104
5295
  stdio: "inherit"
5105
5296
  });
@@ -5107,81 +5298,102 @@ async function spawnInTmux(projects, sessionName) {
5107
5298
  }
5108
5299
  async function spawnInGnomeTerminal(projects) {
5109
5300
  const args = [];
5110
- for (let i = 0; i < projects.length; i++) {
5111
- const project = projects[i];
5112
- const command = buildClaudeCommand(project);
5113
- if (i === 0) {
5114
- args.push("--tab", "--title", project.projectName, "--", "bash", "-c", `${command}; exec bash`);
5115
- } else {
5116
- args.push("--tab", "--title", project.projectName, "--", "bash", "-c", `${command}; exec bash`);
5301
+ for (const project of projects) {
5302
+ const validation = validateProject(project);
5303
+ if (!validation.valid) {
5304
+ console.error(`Skipping invalid project: ${validation.error}`);
5305
+ continue;
5117
5306
  }
5307
+ const claudeArgs = buildClaudeArgs(project);
5308
+ const safeName = sanitizeProjectName(project.projectName);
5309
+ const wrapperScript = `cd ${escapeShellArg(project.projectPath)} && exec claude ${claudeArgs.map(escapeShellArg).join(" ")}`;
5310
+ args.push(
5311
+ "--tab",
5312
+ "--title",
5313
+ safeName,
5314
+ "--",
5315
+ "bash",
5316
+ "-c",
5317
+ wrapperScript
5318
+ );
5319
+ }
5320
+ if (args.length > 0) {
5321
+ spawn2("gnome-terminal", args, {
5322
+ stdio: "ignore",
5323
+ detached: true
5324
+ }).unref();
5118
5325
  }
5119
- spawn2("gnome-terminal", args, {
5120
- stdio: "ignore",
5121
- detached: true
5122
- }).unref();
5123
5326
  }
5124
5327
  async function spawnInKitty(projects) {
5125
- if (process.env.KITTY_WINDOW_ID) {
5126
- for (const project of projects) {
5127
- const command = buildClaudeCommand(project);
5328
+ const isInsideKitty = !!process.env.KITTY_WINDOW_ID;
5329
+ for (let i = 0; i < projects.length; i++) {
5330
+ const project = projects[i];
5331
+ const validation = validateProject(project);
5332
+ if (!validation.valid) {
5333
+ console.error(`Skipping invalid project: ${validation.error}`);
5334
+ continue;
5335
+ }
5336
+ const claudeArgs = buildClaudeArgs(project);
5337
+ const safeName = sanitizeProjectName(project.projectName);
5338
+ if (isInsideKitty) {
5128
5339
  spawn2("kitten", [
5129
5340
  "@",
5130
5341
  "launch",
5131
5342
  "--type=tab",
5132
5343
  "--tab-title",
5133
- project.projectName,
5344
+ safeName,
5134
5345
  "--cwd",
5135
5346
  project.projectPath,
5136
- "bash",
5137
- "-c",
5138
- command
5347
+ "claude",
5348
+ ...claudeArgs
5139
5349
  ], { stdio: "inherit" });
5350
+ } else {
5351
+ if (i === 0) {
5352
+ const kittyArgs = [
5353
+ "--title",
5354
+ safeName,
5355
+ "--directory",
5356
+ project.projectPath,
5357
+ "claude",
5358
+ ...claudeArgs
5359
+ ];
5360
+ spawn2("kitty", kittyArgs, {
5361
+ stdio: "ignore",
5362
+ detached: true
5363
+ }).unref();
5364
+ } else {
5365
+ spawn2("kitty", [
5366
+ "--title",
5367
+ safeName,
5368
+ "--directory",
5369
+ project.projectPath,
5370
+ "claude",
5371
+ ...claudeArgs
5372
+ ], {
5373
+ stdio: "ignore",
5374
+ detached: true
5375
+ }).unref();
5376
+ }
5140
5377
  }
5141
- } else {
5142
- const firstProject = projects[0];
5143
- const firstCommand = buildClaudeCommand(firstProject);
5144
- const args = [
5145
- "--title",
5146
- firstProject.projectName,
5147
- "--directory",
5148
- firstProject.projectPath,
5149
- "bash",
5150
- "-c",
5151
- firstCommand
5152
- ];
5153
- for (let i = 1; i < projects.length; i++) {
5154
- const project = projects[i];
5155
- const command = buildClaudeCommand(project);
5156
- args.push(
5157
- "--new-tab",
5158
- "--title",
5159
- project.projectName,
5160
- "--directory",
5161
- project.projectPath,
5162
- "bash",
5163
- "-c",
5164
- command
5165
- );
5166
- }
5167
- spawn2("kitty", args, {
5168
- stdio: "ignore",
5169
- detached: true
5170
- }).unref();
5171
5378
  }
5172
5379
  }
5173
5380
  async function spawnInAlacritty(projects) {
5174
5381
  for (const project of projects) {
5175
- const command = buildClaudeCommand(project);
5382
+ const validation = validateProject(project);
5383
+ if (!validation.valid) {
5384
+ console.error(`Skipping invalid project: ${validation.error}`);
5385
+ continue;
5386
+ }
5387
+ const claudeArgs = buildClaudeArgs(project);
5388
+ const safeName = sanitizeProjectName(project.projectName);
5176
5389
  spawn2("alacritty", [
5177
5390
  "--title",
5178
- project.projectName,
5391
+ safeName,
5179
5392
  "--working-directory",
5180
5393
  project.projectPath,
5181
5394
  "-e",
5182
- "bash",
5183
- "-c",
5184
- command
5395
+ "claude",
5396
+ ...claudeArgs
5185
5397
  ], {
5186
5398
  stdio: "ignore",
5187
5399
  detached: true
@@ -5190,15 +5402,20 @@ async function spawnInAlacritty(projects) {
5190
5402
  }
5191
5403
  async function spawnSequential(projects) {
5192
5404
  const project = projects[0];
5193
- const command = buildClaudeCommand(project);
5194
- console.log(`Starting: ${project.projectName}`);
5195
- console.log(`Path: ${project.projectPath}`);
5405
+ const validation = validateProject(project);
5406
+ if (!validation.valid) {
5407
+ console.error(`Cannot start: ${validation.error}`);
5408
+ return;
5409
+ }
5410
+ console.log(`Starting: ${sanitizeForLog(project.projectName)}`);
5411
+ console.log(`Path: ${sanitizeForLog(project.projectPath)}`);
5196
5412
  if (projects.length > 1) {
5197
5413
  console.log(`
5198
5414
  Note: ${projects.length - 1} additional projects cannot be started in parallel.`);
5199
5415
  console.log("Install tmux for multi-project support: sudo apt install tmux");
5200
5416
  }
5201
- spawn2("bash", ["-c", command], {
5417
+ const claudeArgs = buildClaudeArgs(project);
5418
+ spawn2("claude", claudeArgs, {
5202
5419
  stdio: "inherit",
5203
5420
  cwd: project.projectPath
5204
5421
  });
@@ -5211,27 +5428,38 @@ async function spawnInDocker(projects) {
5211
5428
  }
5212
5429
  if (projects.length === 1) {
5213
5430
  const project = projects[0];
5214
- console.log(`Starting in Docker: ${project.projectName}`);
5215
- console.log(`Path: ${project.projectPath}`);
5216
- console.log(`Image: ${dockerSettings.defaultImage}`);
5431
+ const validation = validateProject(project);
5432
+ if (!validation.valid) {
5433
+ console.error(`Cannot start: ${validation.error}`);
5434
+ return;
5435
+ }
5436
+ console.log(`Starting in Docker: ${sanitizeForLog(project.projectName)}`);
5437
+ console.log(`Path: ${sanitizeForLog(project.projectPath)}`);
5438
+ console.log(`Image: ${sanitizeForLog(dockerSettings.defaultImage)}`);
5217
5439
  await dockerService.runClaudeInContainer(project, dockerSettings);
5218
5440
  return;
5219
5441
  }
5220
5442
  if (isInsideTmux()) {
5221
5443
  for (let i = 0; i < projects.length; i++) {
5222
5444
  const project = projects[i];
5445
+ const validation = validateProject(project);
5446
+ if (!validation.valid) {
5447
+ console.error(`Skipping invalid project: ${validation.error}`);
5448
+ continue;
5449
+ }
5223
5450
  if (i === 0) {
5224
- console.log(`Starting in Docker: ${project.projectName}`);
5451
+ console.log(`Starting in Docker: ${sanitizeForLog(project.projectName)}`);
5225
5452
  await dockerService.runClaudeInContainer(project, dockerSettings);
5226
5453
  } else {
5227
- const containerName = `shiva-${project.projectName.replace(/[^a-zA-Z0-9-]/g, "-")}-${Date.now()}`;
5228
- const cmd = dockerService.getDockerInfo().runtime === "podman" ? "podman" : "docker";
5454
+ const safeName = sanitizeProjectName(project.projectName);
5455
+ const containerName = `shiva-${safeName}-${Date.now()}`;
5229
5456
  spawnSync2("tmux", [
5230
5457
  "new-window",
5231
5458
  "-n",
5232
- project.projectName
5459
+ safeName
5233
5460
  ], { stdio: "inherit" });
5234
- const dockerCmd = buildDockerCommand(project, dockerSettings, containerName);
5461
+ const dockerArgs = buildSecureDockerArgs(project, dockerSettings, containerName);
5462
+ const dockerCmd = dockerArgs.join(" ");
5235
5463
  spawnSync2("tmux", [
5236
5464
  "send-keys",
5237
5465
  dockerCmd,
@@ -5242,37 +5470,59 @@ async function spawnInDocker(projects) {
5242
5470
  } else {
5243
5471
  console.log(`Starting ${projects.length} projects in Docker containers...`);
5244
5472
  for (const project of projects) {
5245
- console.log(` - ${project.projectName}`);
5473
+ const validation2 = validateProject(project);
5474
+ if (validation2.valid) {
5475
+ console.log(` - ${sanitizeForLog(project.projectName)}`);
5476
+ }
5246
5477
  }
5247
5478
  const firstProject = projects[0];
5248
- console.log(`
5249
- Attaching to: ${firstProject.projectName}`);
5250
- for (let i = 1; i < projects.length; i++) {
5251
- const project = projects[i];
5252
- console.log(`Background container started for: ${project.projectName}`);
5479
+ const validation = validateProject(firstProject);
5480
+ if (!validation.valid) {
5481
+ console.error(`Cannot start: ${validation.error}`);
5482
+ return;
5253
5483
  }
5484
+ console.log(`
5485
+ Attaching to: ${sanitizeForLog(firstProject.projectName)}`);
5254
5486
  await dockerService.runClaudeInContainer(firstProject, dockerSettings);
5255
5487
  }
5256
5488
  }
5257
- function buildDockerCommand(project, settings, containerName) {
5489
+ function buildSecureDockerArgs(project, settings, containerName) {
5258
5490
  const cmd = dockerService.getDockerInfo().runtime === "podman" ? "podman" : "docker";
5259
- const os7 = __require("os");
5260
- const path15 = __require("path");
5261
- let dockerCmd = `${cmd} run -it --rm --name ${containerName} --workdir /workspace`;
5262
- dockerCmd += ` -v "${project.projectPath}:/workspace:rw"`;
5263
- dockerCmd += ` -v "${path15.join(os7.homedir(), ".claude")}:/root/.claude:rw"`;
5264
- dockerCmd += ` -v "${path15.join(os7.homedir(), ".config", "shiva-code")}:/root/.config/shiva-code:rw"`;
5265
- for (const [host, container] of Object.entries(settings.volumeMounts || {})) {
5266
- dockerCmd += ` -v "${host}:${container}:rw"`;
5491
+ const homeDir = os3.homedir();
5492
+ const args = [
5493
+ cmd,
5494
+ "run",
5495
+ "-it",
5496
+ "--rm",
5497
+ "--name",
5498
+ containerName,
5499
+ "--workdir",
5500
+ "/workspace",
5501
+ "-v",
5502
+ `${project.projectPath}:/workspace:rw`,
5503
+ "-v",
5504
+ `${path3.join(homeDir, ".claude")}:/root/.claude:rw`,
5505
+ "-v",
5506
+ `${path3.join(homeDir, ".config", "shiva-code")}:/root/.config/shiva-code:rw`
5507
+ ];
5508
+ for (const [hostPath, containerPath] of Object.entries(settings.volumeMounts || {})) {
5509
+ if (isValidProjectPath(hostPath)) {
5510
+ args.push("-v", `${hostPath}:${containerPath}:rw`);
5511
+ }
5267
5512
  }
5268
5513
  for (const [key, value] of Object.entries(settings.environment || {})) {
5269
- dockerCmd += ` -e "${key}=${value}"`;
5514
+ if (/^[A-Z_][A-Z0-9_]*$/.test(key)) {
5515
+ args.push("-e", `${key}=${value}`);
5516
+ }
5270
5517
  }
5271
- dockerCmd += ` ${settings.defaultImage} claude`;
5518
+ args.push(settings.defaultImage, "claude");
5272
5519
  if (project.sessionId && !project.newSession) {
5273
- dockerCmd += ` --resume ${project.sessionId}`;
5520
+ args.push("--resume", project.sessionId);
5274
5521
  }
5275
- return dockerCmd;
5522
+ return args;
5523
+ }
5524
+ function escapeShellArg(arg) {
5525
+ return `'${arg.replace(/'/g, "'\\''")}'`;
5276
5526
  }
5277
5527
  async function spawnProjects(projects, terminal) {
5278
5528
  if (projects.length === 0) {
@@ -5320,9 +5570,9 @@ function getTerminalName(terminal) {
5320
5570
  }
5321
5571
 
5322
5572
  // src/services/sandbox/sandbox.ts
5323
- import { execSync as execSync3 } from "child_process";
5573
+ import { execSync } from "child_process";
5324
5574
  import { existsSync as existsSync6, mkdirSync, rmSync, readdirSync as readdirSync2, copyFileSync, readFileSync as readFileSync2 } from "fs";
5325
- import { join as join3, dirname } from "path";
5575
+ import { join as join4, dirname } from "path";
5326
5576
  import { randomUUID } from "crypto";
5327
5577
  import Conf3 from "conf";
5328
5578
  var DEFAULT_SANDBOX_CONFIG = {
@@ -5361,10 +5611,10 @@ var SandboxService = class {
5361
5611
  /**
5362
5612
  * Check if a path is a git repository
5363
5613
  */
5364
- isGitRepo(path15) {
5614
+ isGitRepo(path16) {
5365
5615
  try {
5366
- execSync3("git rev-parse --git-dir", {
5367
- cwd: path15,
5616
+ execSync("git rev-parse --git-dir", {
5617
+ cwd: path16,
5368
5618
  stdio: "pipe"
5369
5619
  });
5370
5620
  return true;
@@ -5377,11 +5627,11 @@ var SandboxService = class {
5377
5627
  */
5378
5628
  isDockerAvailable() {
5379
5629
  try {
5380
- execSync3("docker info", { stdio: "pipe" });
5630
+ execSync("docker info", { stdio: "pipe" });
5381
5631
  return true;
5382
5632
  } catch {
5383
5633
  try {
5384
- execSync3("podman info", { stdio: "pipe" });
5634
+ execSync("podman info", { stdio: "pipe" });
5385
5635
  return true;
5386
5636
  } catch {
5387
5637
  return false;
@@ -5404,7 +5654,7 @@ var SandboxService = class {
5404
5654
  * Get the .shiva/sandbox directory path
5405
5655
  */
5406
5656
  getSandboxDir(projectPath) {
5407
- return join3(projectPath, ".shiva", "sandbox");
5657
+ return join4(projectPath, ".shiva", "sandbox");
5408
5658
  }
5409
5659
  /**
5410
5660
  * Generate a unique session ID
@@ -5466,13 +5716,13 @@ var SandboxService = class {
5466
5716
  */
5467
5717
  async createWorktreeSandbox(projectPath, sessionId) {
5468
5718
  const sandboxDir = this.getSandboxDir(projectPath);
5469
- const sandboxPath = join3(sandboxDir, `session-${sessionId}`);
5719
+ const sandboxPath = join4(sandboxDir, `session-${sessionId}`);
5470
5720
  mkdirSync(dirname(sandboxPath), { recursive: true });
5471
- const currentRef = execSync3("git rev-parse HEAD", {
5721
+ const currentRef = execSync("git rev-parse HEAD", {
5472
5722
  cwd: projectPath,
5473
5723
  encoding: "utf-8"
5474
5724
  }).trim();
5475
- execSync3(`git worktree add --detach "${sandboxPath}" ${currentRef}`, {
5725
+ execSync(`git worktree add --detach "${sandboxPath}" ${currentRef}`, {
5476
5726
  cwd: projectPath,
5477
5727
  stdio: "pipe"
5478
5728
  });
@@ -5484,14 +5734,14 @@ var SandboxService = class {
5484
5734
  */
5485
5735
  async copyUntrackedFiles(projectPath, sandboxPath) {
5486
5736
  try {
5487
- const untrackedOutput = execSync3("git ls-files --others --exclude-standard", {
5737
+ const untrackedOutput = execSync("git ls-files --others --exclude-standard", {
5488
5738
  cwd: projectPath,
5489
5739
  encoding: "utf-8"
5490
5740
  });
5491
5741
  const untrackedFiles = untrackedOutput.split("\n").filter((f) => f.trim().length > 0);
5492
5742
  for (const file of untrackedFiles) {
5493
- const srcPath = join3(projectPath, file);
5494
- const destPath = join3(sandboxPath, file);
5743
+ const srcPath = join4(projectPath, file);
5744
+ const destPath = join4(sandboxPath, file);
5495
5745
  if (!existsSync6(srcPath)) continue;
5496
5746
  const config2 = this.getConfig();
5497
5747
  if (config2.excludePaths.some((excluded) => file.startsWith(excluded))) {
@@ -5511,7 +5761,7 @@ var SandboxService = class {
5511
5761
  */
5512
5762
  async createCopySandbox(projectPath, sessionId, excludePaths) {
5513
5763
  const sandboxDir = this.getSandboxDir(projectPath);
5514
- const sandboxPath = join3(sandboxDir, `session-${sessionId}`);
5764
+ const sandboxPath = join4(sandboxDir, `session-${sessionId}`);
5515
5765
  mkdirSync(sandboxPath, { recursive: true });
5516
5766
  this.copyDirectory(projectPath, sandboxPath, excludePaths);
5517
5767
  return sandboxPath;
@@ -5522,8 +5772,8 @@ var SandboxService = class {
5522
5772
  copyDirectory(src, dest, excludePaths) {
5523
5773
  const entries = readdirSync2(src, { withFileTypes: true });
5524
5774
  for (const entry of entries) {
5525
- const srcPath = join3(src, entry.name);
5526
- const destPath = join3(dest, entry.name);
5775
+ const srcPath = join4(src, entry.name);
5776
+ const destPath = join4(dest, entry.name);
5527
5777
  if (excludePaths.includes(entry.name)) {
5528
5778
  continue;
5529
5779
  }
@@ -5543,9 +5793,9 @@ var SandboxService = class {
5543
5793
  */
5544
5794
  async createDockerOverlaySandbox(projectPath, sessionId) {
5545
5795
  const sandboxDir = this.getSandboxDir(projectPath);
5546
- const sandboxPath = join3(sandboxDir, `session-${sessionId}`);
5547
- const upperDir = join3(sandboxDir, `upper-${sessionId}`);
5548
- const workDir = join3(sandboxDir, `work-${sessionId}`);
5796
+ const sandboxPath = join4(sandboxDir, `session-${sessionId}`);
5797
+ const upperDir = join4(sandboxDir, `upper-${sessionId}`);
5798
+ const workDir = join4(sandboxDir, `work-${sessionId}`);
5549
5799
  mkdirSync(sandboxPath, { recursive: true });
5550
5800
  mkdirSync(upperDir, { recursive: true });
5551
5801
  mkdirSync(workDir, { recursive: true });
@@ -5612,7 +5862,7 @@ var SandboxService = class {
5612
5862
  }
5613
5863
  if (session.mode === "worktree") {
5614
5864
  try {
5615
- execSync3(`git worktree remove "${session.sandboxPath}" --force`, {
5865
+ execSync(`git worktree remove "${session.sandboxPath}" --force`, {
5616
5866
  cwd: session.projectPath,
5617
5867
  stdio: "pipe"
5618
5868
  });
@@ -5621,7 +5871,7 @@ var SandboxService = class {
5621
5871
  rmSync(session.sandboxPath, { recursive: true, force: true });
5622
5872
  }
5623
5873
  try {
5624
- execSync3("git worktree prune", {
5874
+ execSync("git worktree prune", {
5625
5875
  cwd: session.projectPath,
5626
5876
  stdio: "pipe"
5627
5877
  });
@@ -5650,11 +5900,11 @@ var SandboxService = class {
5650
5900
  }
5651
5901
  const changes = [];
5652
5902
  if (session.mode === "worktree") {
5653
- const diffOutput = execSync3("git diff --stat --numstat HEAD", {
5903
+ const diffOutput = execSync("git diff --stat --numstat HEAD", {
5654
5904
  cwd: session.sandboxPath,
5655
5905
  encoding: "utf-8"
5656
5906
  });
5657
- const untrackedOutput = execSync3("git ls-files --others --exclude-standard", {
5907
+ const untrackedOutput = execSync("git ls-files --others --exclude-standard", {
5658
5908
  cwd: session.sandboxPath,
5659
5909
  encoding: "utf-8"
5660
5910
  });
@@ -5673,7 +5923,7 @@ var SandboxService = class {
5673
5923
  }
5674
5924
  const untrackedFiles = untrackedOutput.split("\n").filter((f) => f.trim().length > 0);
5675
5925
  for (const file of untrackedFiles) {
5676
- const filePath = join3(session.sandboxPath, file);
5926
+ const filePath = join4(session.sandboxPath, file);
5677
5927
  if (existsSync6(filePath)) {
5678
5928
  const content = readFileSync2(filePath, "utf-8");
5679
5929
  const lineCount = content.split("\n").length;
@@ -5708,7 +5958,7 @@ var SandboxService = class {
5708
5958
  async compareDirectories(originalPath, sandboxPath, relativePath, changes) {
5709
5959
  const config2 = this.getConfig();
5710
5960
  const sandboxEntries = /* @__PURE__ */ new Set();
5711
- const sandboxFullPath = join3(sandboxPath, relativePath);
5961
+ const sandboxFullPath = join4(sandboxPath, relativePath);
5712
5962
  if (existsSync6(sandboxFullPath)) {
5713
5963
  const entries = readdirSync2(sandboxFullPath, { withFileTypes: true });
5714
5964
  for (const entry of entries) {
@@ -5716,9 +5966,9 @@ var SandboxService = class {
5716
5966
  continue;
5717
5967
  }
5718
5968
  sandboxEntries.add(entry.name);
5719
- const entryRelPath = relativePath ? join3(relativePath, entry.name) : entry.name;
5720
- const originalEntryPath = join3(originalPath, entryRelPath);
5721
- const sandboxEntryPath = join3(sandboxPath, entryRelPath);
5969
+ const entryRelPath = relativePath ? join4(relativePath, entry.name) : entry.name;
5970
+ const originalEntryPath = join4(originalPath, entryRelPath);
5971
+ const sandboxEntryPath = join4(sandboxPath, entryRelPath);
5722
5972
  if (entry.isDirectory()) {
5723
5973
  if (!existsSync6(originalEntryPath)) {
5724
5974
  await this.countNewDirectoryChanges(sandboxEntryPath, entryRelPath, changes);
@@ -5752,7 +6002,7 @@ var SandboxService = class {
5752
6002
  }
5753
6003
  }
5754
6004
  }
5755
- const originalFullPath = join3(originalPath, relativePath);
6005
+ const originalFullPath = join4(originalPath, relativePath);
5756
6006
  if (existsSync6(originalFullPath)) {
5757
6007
  const entries = readdirSync2(originalFullPath, { withFileTypes: true });
5758
6008
  for (const entry of entries) {
@@ -5760,8 +6010,8 @@ var SandboxService = class {
5760
6010
  continue;
5761
6011
  }
5762
6012
  if (!sandboxEntries.has(entry.name)) {
5763
- const entryRelPath = relativePath ? join3(relativePath, entry.name) : entry.name;
5764
- const originalEntryPath = join3(originalPath, entryRelPath);
6013
+ const entryRelPath = relativePath ? join4(relativePath, entry.name) : entry.name;
6014
+ const originalEntryPath = join4(originalPath, entryRelPath);
5765
6015
  if (entry.isDirectory()) {
5766
6016
  await this.countDeletedDirectoryChanges(originalEntryPath, entryRelPath, changes);
5767
6017
  } else {
@@ -5804,8 +6054,8 @@ var SandboxService = class {
5804
6054
  const config2 = this.getConfig();
5805
6055
  for (const entry of entries) {
5806
6056
  if (config2.excludePaths.includes(entry.name)) continue;
5807
- const entryRelPath = join3(relativePath, entry.name);
5808
- const entryPath = join3(dirPath, entry.name);
6057
+ const entryRelPath = join4(relativePath, entry.name);
6058
+ const entryPath = join4(dirPath, entry.name);
5809
6059
  if (entry.isDirectory()) {
5810
6060
  await this.countNewDirectoryChanges(entryPath, entryRelPath, changes);
5811
6061
  } else {
@@ -5828,8 +6078,8 @@ var SandboxService = class {
5828
6078
  const config2 = this.getConfig();
5829
6079
  for (const entry of entries) {
5830
6080
  if (config2.excludePaths.includes(entry.name)) continue;
5831
- const entryRelPath = join3(relativePath, entry.name);
5832
- const entryPath = join3(dirPath, entry.name);
6081
+ const entryRelPath = join4(relativePath, entry.name);
6082
+ const entryPath = join4(dirPath, entry.name);
5833
6083
  if (entry.isDirectory()) {
5834
6084
  await this.countDeletedDirectoryChanges(entryPath, entryRelPath, changes);
5835
6085
  } else {
@@ -5854,13 +6104,13 @@ var SandboxService = class {
5854
6104
  }
5855
6105
  if (session.mode === "worktree") {
5856
6106
  try {
5857
- const diff = execSync3(`git diff HEAD -- "${filePath}"`, {
6107
+ const diff = execSync(`git diff HEAD -- "${filePath}"`, {
5858
6108
  cwd: session.sandboxPath,
5859
6109
  encoding: "utf-8"
5860
6110
  });
5861
6111
  return diff || "(no changes)";
5862
6112
  } catch {
5863
- const sandboxFilePath2 = join3(session.sandboxPath, filePath);
6113
+ const sandboxFilePath2 = join4(session.sandboxPath, filePath);
5864
6114
  if (existsSync6(sandboxFilePath2)) {
5865
6115
  const content = readFileSync2(sandboxFilePath2, "utf-8");
5866
6116
  return `+++ ${filePath} (new file)
@@ -5869,8 +6119,8 @@ ${content.split("\n").map((l) => `+ ${l}`).join("\n")}`;
5869
6119
  return "(file not found)";
5870
6120
  }
5871
6121
  }
5872
- const originalPath = join3(session.projectPath, filePath);
5873
- const sandboxFilePath = join3(session.sandboxPath, filePath);
6122
+ const originalPath = join4(session.projectPath, filePath);
6123
+ const sandboxFilePath = join4(session.sandboxPath, filePath);
5874
6124
  const originalExists = existsSync6(originalPath);
5875
6125
  const sandboxExists = existsSync6(sandboxFilePath);
5876
6126
  if (!originalExists && sandboxExists) {
@@ -5928,8 +6178,8 @@ ${content.split("\n").map((l) => `- ${l}`).join("\n")}`;
5928
6178
  for (const filePath of pathsToApply) {
5929
6179
  const change = diff.changes.find((c) => c.path === filePath);
5930
6180
  if (!change) continue;
5931
- const srcPath = join3(session.sandboxPath, filePath);
5932
- const destPath = join3(session.projectPath, filePath);
6181
+ const srcPath = join4(session.sandboxPath, filePath);
6182
+ const destPath = join4(session.projectPath, filePath);
5933
6183
  if (change.type === "deleted") {
5934
6184
  if (existsSync6(destPath)) {
5935
6185
  rmSync(destPath, { force: true });
@@ -5966,8 +6216,8 @@ ${content.split("\n").map((l) => `- ${l}`).join("\n")}`;
5966
6216
  throw new Error(`Sandbox ${sessionId} not found`);
5967
6217
  }
5968
6218
  for (const filePath of filePaths) {
5969
- const sandboxFilePath = join3(session.sandboxPath, filePath);
5970
- const originalPath = join3(session.projectPath, filePath);
6219
+ const sandboxFilePath = join4(session.sandboxPath, filePath);
6220
+ const originalPath = join4(session.projectPath, filePath);
5971
6221
  if (existsSync6(originalPath)) {
5972
6222
  copyFileSync(originalPath, sandboxFilePath);
5973
6223
  } else {
@@ -6064,7 +6314,7 @@ var startCommand = new Command10("start").description("Projekte starten (mit Git
6064
6314
  const allProjects = await getAllClaudeProjects();
6065
6315
  const launches = [];
6066
6316
  for (const projektArg of projekte) {
6067
- const absolutePath = path3.resolve(projektArg);
6317
+ const absolutePath = path4.resolve(projektArg);
6068
6318
  if (fs.existsSync(absolutePath) && fs.statSync(absolutePath).isDirectory()) {
6069
6319
  const project = findProjectFromArray(allProjects, absolutePath);
6070
6320
  const latestSession = project?.latestSession;
@@ -6814,10 +7064,10 @@ import ora9 from "ora";
6814
7064
 
6815
7065
  // src/services/data/tags.ts
6816
7066
  import * as fs4 from "fs";
6817
- import * as path4 from "path";
6818
- import * as os3 from "os";
7067
+ import * as path5 from "path";
7068
+ import * as os4 from "os";
6819
7069
  function getTagsPath() {
6820
- return path4.join(os3.homedir(), ".shiva", "tags.json");
7070
+ return path5.join(os4.homedir(), ".shiva", "tags.json");
6821
7071
  }
6822
7072
  function loadTagsData() {
6823
7073
  const filepath = getTagsPath();
@@ -6833,7 +7083,7 @@ function loadTagsData() {
6833
7083
  }
6834
7084
  function saveTagsData(data) {
6835
7085
  const filepath = getTagsPath();
6836
- const dir = path4.dirname(filepath);
7086
+ const dir = path5.dirname(filepath);
6837
7087
  if (!fs4.existsSync(dir)) {
6838
7088
  fs4.mkdirSync(dir, { recursive: true });
6839
7089
  }
@@ -7085,10 +7335,10 @@ var sessionsCommand = new Command13("sessions").description("Alle Claude Code Se
7085
7335
  log.dim(`Total: ${stats.activeProjects} Projekte, ${stats.totalSessions} Sessions`);
7086
7336
  log.newline();
7087
7337
  }));
7088
- async function checkProjectExists(path15) {
7338
+ async function checkProjectExists(path16) {
7089
7339
  try {
7090
7340
  const fs15 = await import("fs");
7091
- return fs15.existsSync(path15);
7341
+ return fs15.existsSync(path16);
7092
7342
  } catch {
7093
7343
  return false;
7094
7344
  }
@@ -7349,10 +7599,10 @@ sessionCommand.command("apply").description("Sandbox-\xC4nderungen \xFCbernehmen
7349
7599
  log.header("Folgende \xC4nderungen werden \xFCbernommen:");
7350
7600
  log.newline();
7351
7601
  const pathsToApply = selectedPaths || diff.changes.map((c) => c.path);
7352
- for (const path15 of pathsToApply) {
7353
- const change = diff.changes.find((c) => c.path === path15);
7602
+ for (const path16 of pathsToApply) {
7603
+ const change = diff.changes.find((c) => c.path === path16);
7354
7604
  if (change) {
7355
- console.log(` ${formatChangeType(change.type)} ${path15}`);
7605
+ console.log(` ${formatChangeType(change.type)} ${path16}`);
7356
7606
  }
7357
7607
  }
7358
7608
  log.newline();
@@ -7547,7 +7797,7 @@ sessionCommand.command("config").description("Sandbox-Konfiguration verwalten").
7547
7797
  import { Command as Command15 } from "commander";
7548
7798
  import { spawn as spawn5, spawnSync as spawnSync5 } from "child_process";
7549
7799
  import * as fs5 from "fs";
7550
- import * as path5 from "path";
7800
+ import * as path6 from "path";
7551
7801
  import inquirer6 from "inquirer";
7552
7802
  var githubCommand = new Command15("github").description("GitHub Integration verwalten").action(() => {
7553
7803
  showStatus();
@@ -7969,9 +8219,9 @@ githubCommand.command("git-hook").description("Git Hooks f\xFCr Branch-Session I
7969
8219
  log.error("Kein Git Repository");
7970
8220
  return;
7971
8221
  }
7972
- const gitDir = path5.join(cwd, ".git");
7973
- const hooksDir = path5.join(gitDir, "hooks");
7974
- const hookFile = path5.join(hooksDir, "post-checkout");
8222
+ const gitDir = path6.join(cwd, ".git");
8223
+ const hooksDir = path6.join(gitDir, "hooks");
8224
+ const hookFile = path6.join(hooksDir, "post-checkout");
7975
8225
  if (options.uninstall) {
7976
8226
  if (fs5.existsSync(hookFile)) {
7977
8227
  const content = fs5.readFileSync(hookFile, "utf-8");
@@ -8045,13 +8295,13 @@ githubCommand.command("map").description("Aktuellen Branch mit einer Session ver
8045
8295
  initShivaDir(cwd);
8046
8296
  }
8047
8297
  if (!sessionId) {
8048
- const { findProject: findProject3 } = await import("./manager-LXNF7QWT.js");
8298
+ const { findProject: findProject3 } = await import("./manager-ZPQWG7E6.js");
8049
8299
  const project = await findProject3(cwd);
8050
8300
  if (!project || project.sessions.length === 0) {
8051
8301
  log.error("Keine Sessions f\xFCr dieses Projekt gefunden");
8052
8302
  return;
8053
8303
  }
8054
- const { formatDate: formatDate2 } = await import("./manager-LXNF7QWT.js");
8304
+ const { formatDate: formatDate2 } = await import("./manager-ZPQWG7E6.js");
8055
8305
  const choices = project.sessions.slice(0, 10).map((s) => {
8056
8306
  const time = formatDate2(s.modified);
8057
8307
  const msgs = `${s.messageCount} msgs`;
@@ -8360,7 +8610,7 @@ var prsCommand = new Command17("prs").description("GitHub Pull Requests \xFCber
8360
8610
  import { Command as Command18 } from "commander";
8361
8611
  import { readFile } from "fs/promises";
8362
8612
  import { existsSync as existsSync12 } from "fs";
8363
- import { join as join6, resolve as resolve7 } from "path";
8613
+ import { join as join7, resolve as resolve7 } from "path";
8364
8614
 
8365
8615
  // src/services/security/package-scanner.ts
8366
8616
  import Conf4 from "conf";
@@ -9713,9 +9963,9 @@ async function parsePackageJson(filePath) {
9713
9963
  }
9714
9964
  const dir = resolve7(filePath, "..");
9715
9965
  let manager = "npm";
9716
- if (existsSync12(join6(dir, "pnpm-lock.yaml"))) {
9966
+ if (existsSync12(join7(dir, "pnpm-lock.yaml"))) {
9717
9967
  manager = "pnpm";
9718
- } else if (existsSync12(join6(dir, "yarn.lock"))) {
9968
+ } else if (existsSync12(join7(dir, "yarn.lock"))) {
9719
9969
  manager = "yarn";
9720
9970
  }
9721
9971
  return { packages, manager };
@@ -9816,15 +10066,15 @@ var scanCommand = new Command18("scan").description("Scanne Packages auf Sicherh
9816
10066
  }
9817
10067
  if (options.project) {
9818
10068
  const cwd = process.cwd();
9819
- if (existsSync12(join6(cwd, "package.json"))) {
9820
- const parsed = await parsePackageJson(join6(cwd, "package.json"));
10069
+ if (existsSync12(join7(cwd, "package.json"))) {
10070
+ const parsed = await parsePackageJson(join7(cwd, "package.json"));
9821
10071
  packagesToScan = parsed.packages;
9822
10072
  manager = parsed.manager;
9823
- } else if (existsSync12(join6(cwd, "requirements.txt"))) {
9824
- packagesToScan = await parseRequirementsTxt(join6(cwd, "requirements.txt"));
10073
+ } else if (existsSync12(join7(cwd, "requirements.txt"))) {
10074
+ packagesToScan = await parseRequirementsTxt(join7(cwd, "requirements.txt"));
9825
10075
  manager = "pip";
9826
- } else if (existsSync12(join6(cwd, "Cargo.toml"))) {
9827
- packagesToScan = await parseCargoToml(join6(cwd, "Cargo.toml"));
10076
+ } else if (existsSync12(join7(cwd, "Cargo.toml"))) {
10077
+ packagesToScan = await parseCargoToml(join7(cwd, "Cargo.toml"));
9828
10078
  manager = "cargo";
9829
10079
  } else {
9830
10080
  log.error("No dependency file found in current directory");
@@ -10528,10 +10778,10 @@ import { Command as Command20 } from "commander";
10528
10778
  import inquirer8 from "inquirer";
10529
10779
  import ora13 from "ora";
10530
10780
  import * as fs6 from "fs";
10531
- import * as path6 from "path";
10781
+ import * as path7 from "path";
10532
10782
 
10533
10783
  // src/utils/clipboard.ts
10534
- import { execSync as execSync4, spawnSync as spawnSync6 } from "child_process";
10784
+ import { execSync as execSync2, spawnSync as spawnSync6 } from "child_process";
10535
10785
  var CLIPBOARD_COMMANDS = [
10536
10786
  // Wayland (modern Linux)
10537
10787
  { command: "wl-copy", args: [], check: "wl-copy" },
@@ -10544,7 +10794,7 @@ var CLIPBOARD_COMMANDS = [
10544
10794
  ];
10545
10795
  function commandExists2(command) {
10546
10796
  try {
10547
- execSync4(`which ${command}`, { stdio: "ignore" });
10797
+ execSync2(`which ${command}`, { stdio: "ignore" });
10548
10798
  return true;
10549
10799
  } catch {
10550
10800
  return false;
@@ -10590,6 +10840,7 @@ function getClipboardInstallInstructions() {
10590
10840
  }
10591
10841
 
10592
10842
  // src/commands/security/secrets.ts
10843
+ var SECURE_FILE_MODE = 384;
10593
10844
  var secretsCommand = new Command20("secrets").description("API Keys und Secrets verwalten (Cloud Vault)").action(async () => {
10594
10845
  await listSecrets();
10595
10846
  });
@@ -10744,7 +10995,7 @@ secretsCommand.command("remove").alias("rm").alias("delete").description("Secret
10744
10995
  log.error(error instanceof Error ? error.message : "Fehler beim L\xF6schen");
10745
10996
  }
10746
10997
  });
10747
- secretsCommand.command("get").description("Secret-Wert anzeigen (vorsicht!)").argument("<key>", "Secret-Name").option("-p, --project <id>", "Projekt-spezifisches Secret").option("-c, --copy", "In Zwischenablage kopieren").option("-q, --quiet", "Nur Wert ausgeben (f\xFCr Skripte)").action(async (key, options) => {
10998
+ secretsCommand.command("get").description("Secret-Wert anzeigen (maskiert)").argument("<key>", "Secret-Name").option("-p, --project <id>", "Projekt-spezifisches Secret").option("-c, --copy", "In Zwischenablage kopieren").option("-r, --reveal", "Vollst\xE4ndigen Wert anzeigen (Sicherheitsrisiko!)").option("-q, --quiet", "Nur Wert ausgeben (f\xFCr Skripte)").action(async (key, options) => {
10748
10999
  if (!isAuthenticated()) {
10749
11000
  log.errorWithSuggestion(Errors.NOT_AUTHENTICATED());
10750
11001
  return;
@@ -10796,7 +11047,12 @@ secretsCommand.command("get").description("Secret-Wert anzeigen (vorsicht!)").ar
10796
11047
  }
10797
11048
  log.newline();
10798
11049
  log.keyValue("Key", result.key);
10799
- log.keyValue("Value", result.value);
11050
+ if (options.reveal) {
11051
+ log.keyValue("Value", result.value);
11052
+ } else {
11053
+ log.keyValue("Value", maskSecret(result.value));
11054
+ log.dim("Vollst\xE4ndiger Wert mit --reveal oder -r anzeigen");
11055
+ }
10800
11056
  log.keyValue("Scope", result.scope);
10801
11057
  log.newline();
10802
11058
  } catch (error) {
@@ -10828,10 +11084,10 @@ secretsCommand.command("env").description("Secrets als .env Format ausgeben").op
10828
11084
  spinner.stop();
10829
11085
  const envContent = Object.entries(secrets).map(([key, value]) => `${key}="${value}"`).join("\n");
10830
11086
  if (options.output) {
10831
- const { writeFileSync: writeFileSync11 } = await import("fs");
10832
- writeFileSync11(options.output, envContent + "\n", "utf-8");
11087
+ fs6.writeFileSync(options.output, envContent + "\n", { mode: SECURE_FILE_MODE });
10833
11088
  log.success(`Secrets in ${options.output} geschrieben`);
10834
11089
  log.warn("Diese Datei enth\xE4lt sensible Daten!");
11090
+ log.dim(`Datei-Berechtigungen auf ${SECURE_FILE_MODE.toString(8)} gesetzt`);
10835
11091
  } else {
10836
11092
  log.newline();
10837
11093
  console.log(envContent);
@@ -10847,7 +11103,7 @@ secretsCommand.command("import").description(".env Datei in Vault importieren").
10847
11103
  log.errorWithSuggestion(Errors.NOT_AUTHENTICATED());
10848
11104
  return;
10849
11105
  }
10850
- const filePath = path6.resolve(file);
11106
+ const filePath = path7.resolve(file);
10851
11107
  if (!fs6.existsSync(filePath)) {
10852
11108
  log.errorWithSuggestion(Errors.FILE_NOT_FOUND(file));
10853
11109
  return;
@@ -10880,7 +11136,7 @@ secretsCommand.command("import").description(".env Datei in Vault importieren").
10880
11136
  log.listItem(`${secret.key} = ${preview}`);
10881
11137
  }
10882
11138
  log.newline();
10883
- log.dim(`${secrets.length} Secret(s) aus ${path6.basename(file)}`);
11139
+ log.dim(`${secrets.length} Secret(s) aus ${path7.basename(file)}`);
10884
11140
  log.newline();
10885
11141
  if (options.dryRun) {
10886
11142
  log.info("Dry-run: Keine \xC4nderungen vorgenommen");
@@ -10954,9 +11210,10 @@ secretsCommand.command("export").description("Secrets exportieren").option("-p,
10954
11210
  }).join("\n");
10955
11211
  }
10956
11212
  if (options.output) {
10957
- fs6.writeFileSync(options.output, output + "\n", "utf-8");
11213
+ fs6.writeFileSync(options.output, output + "\n", { mode: SECURE_FILE_MODE });
10958
11214
  log.success(`Secrets in ${options.output} geschrieben`);
10959
11215
  log.warn("Diese Datei enth\xE4lt sensible Daten!");
11216
+ log.dim(`Datei-Berechtigungen auf ${SECURE_FILE_MODE.toString(8)} gesetzt`);
10960
11217
  log.dim("Zur .gitignore hinzuf\xFCgen!");
10961
11218
  } else {
10962
11219
  log.newline();
@@ -10976,22 +11233,22 @@ import ora14 from "ora";
10976
11233
 
10977
11234
  // src/services/data/memory.ts
10978
11235
  import * as fs7 from "fs";
10979
- import * as path7 from "path";
10980
- import * as os4 from "os";
11236
+ import * as path8 from "path";
11237
+ import * as os5 from "os";
10981
11238
  function findAllClaudeMdFiles() {
10982
11239
  const results = [];
10983
- const sessionsPath = path7.join(os4.homedir(), ".claude", "projects");
11240
+ const sessionsPath = path8.join(os5.homedir(), ".claude", "projects");
10984
11241
  if (fs7.existsSync(sessionsPath)) {
10985
11242
  const dirs = fs7.readdirSync(sessionsPath);
10986
11243
  for (const dir of dirs) {
10987
11244
  const projectPath = decodeProjectPath(dir);
10988
11245
  if (projectPath && fs7.existsSync(projectPath)) {
10989
- const claudeMdPath = path7.join(projectPath, "CLAUDE.md");
11246
+ const claudeMdPath = path8.join(projectPath, "CLAUDE.md");
10990
11247
  if (fs7.existsSync(claudeMdPath)) {
10991
11248
  results.push({
10992
11249
  path: claudeMdPath,
10993
11250
  projectPath,
10994
- projectName: path7.basename(projectPath)
11251
+ projectName: path8.basename(projectPath)
10995
11252
  });
10996
11253
  }
10997
11254
  }
@@ -11103,7 +11360,7 @@ async function searchMemories(query, options = {}) {
11103
11360
  };
11104
11361
  }
11105
11362
  function deleteLocalMemory(projectPath, key) {
11106
- const claudeMdPath = path7.join(projectPath, "CLAUDE.md");
11363
+ const claudeMdPath = path8.join(projectPath, "CLAUDE.md");
11107
11364
  if (!fs7.existsSync(claudeMdPath)) {
11108
11365
  return false;
11109
11366
  }
@@ -11153,7 +11410,7 @@ async function deleteMemory(projectPath, key, options = {}) {
11153
11410
  }
11154
11411
  async function deleteAllProjectMemories(projectPath) {
11155
11412
  const result = { local: 0, cloud: 0 };
11156
- const claudeMdPath = path7.join(projectPath, "CLAUDE.md");
11413
+ const claudeMdPath = path8.join(projectPath, "CLAUDE.md");
11157
11414
  if (fs7.existsSync(claudeMdPath)) {
11158
11415
  try {
11159
11416
  let content = fs7.readFileSync(claudeMdPath, "utf-8");
@@ -11182,8 +11439,8 @@ async function deleteAllProjectMemories(projectPath) {
11182
11439
  return result;
11183
11440
  }
11184
11441
  async function getContextPreview(projectPath) {
11185
- const projectName = path7.basename(projectPath);
11186
- const claudeMdPath = path7.join(projectPath, "CLAUDE.md");
11442
+ const projectName = path8.basename(projectPath);
11443
+ const claudeMdPath = path8.join(projectPath, "CLAUDE.md");
11187
11444
  const preview = {
11188
11445
  projectName,
11189
11446
  projectPath,
@@ -11205,8 +11462,8 @@ async function getContextPreview(projectPath) {
11205
11462
  projectPath
11206
11463
  }));
11207
11464
  }
11208
- const shivaDir = path7.join(projectPath, ".shiva");
11209
- const contextPath = path7.join(shivaDir, "github-context.md");
11465
+ const shivaDir = path8.join(projectPath, ".shiva");
11466
+ const contextPath = path8.join(shivaDir, "github-context.md");
11210
11467
  if (fs7.existsSync(contextPath)) {
11211
11468
  const content = fs7.readFileSync(contextPath, "utf-8");
11212
11469
  preview.githubContext = content;
@@ -11300,7 +11557,7 @@ function escapeRegex2(str) {
11300
11557
 
11301
11558
  // src/commands/memory/forget.ts
11302
11559
  import { Command as Command22 } from "commander";
11303
- import * as path8 from "path";
11560
+ import * as path9 from "path";
11304
11561
  import inquirer9 from "inquirer";
11305
11562
  import ora15 from "ora";
11306
11563
  var forgetCommand = new Command22("forget").description("Memories l\xF6schen (GDPR)").argument("[key]", "Memory-Key zum L\xF6schen").option("-p, --project <path>", "Projekt-Pfad").option("--all", "Alle Memories eines Projekts l\xF6schen").option("--search <query>", "Memories nach Suchbegriff l\xF6schen").option("--local-only", "Nur lokal l\xF6schen").option("--cloud-only", "Nur aus Cloud l\xF6schen").option("-f, --force", "Ohne Best\xE4tigung l\xF6schen").option("--dry-run", "Nur anzeigen, was gel\xF6scht w\xFCrde").action(async (key, options) => {
@@ -11308,7 +11565,7 @@ var forgetCommand = new Command22("forget").description("Memories l\xF6schen (GD
11308
11565
  console.log(colors.orange.bold("SHIVA Code - Forget"));
11309
11566
  console.log(colors.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11310
11567
  log.newline();
11311
- const projectPath = options.project ? path8.resolve(options.project) : process.cwd();
11568
+ const projectPath = options.project ? path9.resolve(options.project) : process.cwd();
11312
11569
  if (options.all) {
11313
11570
  await handleDeleteAll(projectPath, options);
11314
11571
  return;
@@ -11324,7 +11581,7 @@ var forgetCommand = new Command22("forget").description("Memories l\xF6schen (GD
11324
11581
  await handleInteractiveDelete(projectPath, options);
11325
11582
  });
11326
11583
  async function handleDeleteAll(projectPath, options) {
11327
- const projectName = path8.basename(projectPath);
11584
+ const projectName = path9.basename(projectPath);
11328
11585
  console.log(colors.red.bold("\u26A0\uFE0F WARNUNG: Alle Memories l\xF6schen"));
11329
11586
  console.log(colors.dim(`Projekt: ${projectName}`));
11330
11587
  console.log(colors.dim(`Pfad: ${projectPath}`));
@@ -11430,7 +11687,7 @@ async function handleDeleteBySearch(query, projectPath, options) {
11430
11687
  }
11431
11688
  async function handleDeleteKey(key, projectPath, options) {
11432
11689
  console.log(colors.bold(`Memory l\xF6schen: ${key}`));
11433
- console.log(colors.dim(`Projekt: ${path8.basename(projectPath)}`));
11690
+ console.log(colors.dim(`Projekt: ${path9.basename(projectPath)}`));
11434
11691
  log.newline();
11435
11692
  if (options.dryRun) {
11436
11693
  log.info("Dry-run: Keine \xC4nderungen werden vorgenommen");
@@ -11545,12 +11802,12 @@ function truncate(str, maxLen) {
11545
11802
 
11546
11803
  // src/commands/memory/context.ts
11547
11804
  import { Command as Command23 } from "commander";
11548
- import * as path9 from "path";
11805
+ import * as path10 from "path";
11549
11806
  import * as fs8 from "fs";
11550
11807
  import ora16 from "ora";
11551
11808
  var contextCommand = new Command23("context").description("Zeigt was in Claude injected w\xFCrde").option("-d, --dir <path>", "Projektverzeichnis").option("--github", "GitHub Context einschlie\xDFen").option("--secrets", "Secret-Keys anzeigen").option("--raw", "Rohe CLAUDE.md Ausgabe").option("--json", "JSON Ausgabe").option("-s, --size", "Nur Gr\xF6\xDFe anzeigen").action(async (options) => {
11552
- const projectPath = options.dir ? path9.resolve(options.dir) : process.cwd();
11553
- const projectName = path9.basename(projectPath);
11809
+ const projectPath = options.dir ? path10.resolve(options.dir) : process.cwd();
11810
+ const projectName = path10.basename(projectPath);
11554
11811
  if (!fs8.existsSync(projectPath)) {
11555
11812
  log.error(`Verzeichnis nicht gefunden: ${projectPath}`);
11556
11813
  return;
@@ -11934,15 +12191,15 @@ async function resolveSessionId(input) {
11934
12191
 
11935
12192
  // src/commands/memory/export.ts
11936
12193
  import { Command as Command25 } from "commander";
11937
- import * as path11 from "path";
12194
+ import * as path12 from "path";
11938
12195
  import * as fs10 from "fs";
11939
12196
  import ora17 from "ora";
11940
12197
  import inquirer11 from "inquirer";
11941
12198
 
11942
12199
  // src/services/session/export.ts
11943
12200
  import * as fs9 from "fs";
11944
- import * as path10 from "path";
11945
- import { execSync as execSync5 } from "child_process";
12201
+ import * as path11 from "path";
12202
+ import { execSync as execSync3 } from "child_process";
11946
12203
  async function exportSession(sessionId, options = {}) {
11947
12204
  const projects = await getAllClaudeProjects();
11948
12205
  for (const project of projects) {
@@ -11957,7 +12214,7 @@ async function exportSession(sessionId, options = {}) {
11957
12214
  tags: getSessionTags(sessionId)
11958
12215
  };
11959
12216
  if (options.includeTranscript) {
11960
- const transcriptPath = path10.join(
12217
+ const transcriptPath = path11.join(
11961
12218
  getClaudeProjectsPath(),
11962
12219
  encodeProjectPath(project.absolutePath),
11963
12220
  session.sessionId + ".jsonl"
@@ -11967,7 +12224,7 @@ async function exportSession(sessionId, options = {}) {
11967
12224
  }
11968
12225
  }
11969
12226
  if (options.includeConversation && options.includeTranscript) {
11970
- const transcriptPath = path10.join(
12227
+ const transcriptPath = path11.join(
11971
12228
  getClaudeProjectsPath(),
11972
12229
  encodeProjectPath(project.absolutePath),
11973
12230
  session.sessionId + ".jsonl"
@@ -12008,7 +12265,7 @@ async function exportSessions(sessionIds, outputDir, options = {}) {
12008
12265
  const exported = await exportSession(sessionId, options);
12009
12266
  if (exported) {
12010
12267
  const filename = `${exported.projectName}-${sessionId.slice(0, 8)}.json`;
12011
- const filepath = path10.join(outputDir, filename);
12268
+ const filepath = path11.join(outputDir, filename);
12012
12269
  fs9.writeFileSync(filepath, JSON.stringify(exported, null, 2));
12013
12270
  success++;
12014
12271
  } else {
@@ -12038,7 +12295,7 @@ function createBackupArchive(outputPath, projectPaths) {
12038
12295
  if (projectPaths && projectPaths.length > 0) {
12039
12296
  for (const p of projectPaths) {
12040
12297
  const encoded = encodeProjectPath(p);
12041
- const fullPath = path10.join(claudeDir, encoded);
12298
+ const fullPath = path11.join(claudeDir, encoded);
12042
12299
  if (fs9.existsSync(fullPath)) {
12043
12300
  includePaths.push(encoded);
12044
12301
  }
@@ -12046,7 +12303,7 @@ function createBackupArchive(outputPath, projectPaths) {
12046
12303
  } else {
12047
12304
  const entries = fs9.readdirSync(claudeDir);
12048
12305
  includePaths = entries.filter((e) => {
12049
- const stat = fs9.statSync(path10.join(claudeDir, e));
12306
+ const stat = fs9.statSync(path11.join(claudeDir, e));
12050
12307
  return stat.isDirectory();
12051
12308
  });
12052
12309
  }
@@ -12054,7 +12311,7 @@ function createBackupArchive(outputPath, projectPaths) {
12054
12311
  return false;
12055
12312
  }
12056
12313
  const tarArgs = includePaths.join(" ");
12057
- execSync5(`tar -czf "${outputPath}" -C "${claudeDir}" ${tarArgs}`, {
12314
+ execSync3(`tar -czf "${outputPath}" -C "${claudeDir}" ${tarArgs}`, {
12058
12315
  stdio: "ignore"
12059
12316
  });
12060
12317
  return true;
@@ -12066,11 +12323,11 @@ async function importSession(data, targetProjectPath) {
12066
12323
  try {
12067
12324
  const projectPath = targetProjectPath || data.projectPath;
12068
12325
  const encodedPath = encodeProjectPath(projectPath);
12069
- const sessionDir = path10.join(getClaudeProjectsPath(), encodedPath);
12326
+ const sessionDir = path11.join(getClaudeProjectsPath(), encodedPath);
12070
12327
  if (!fs9.existsSync(sessionDir)) {
12071
12328
  fs9.mkdirSync(sessionDir, { recursive: true });
12072
12329
  }
12073
- const sessionFile = path10.join(sessionDir, data.session.sessionId + ".jsonl");
12330
+ const sessionFile = path11.join(sessionDir, data.session.sessionId + ".jsonl");
12074
12331
  if (fs9.existsSync(sessionFile)) {
12075
12332
  return {
12076
12333
  success: false,
@@ -12142,7 +12399,7 @@ async function importSessionsFromDirectory(dirPath, targetProjectPath) {
12142
12399
  let success = 0;
12143
12400
  let failed = 0;
12144
12401
  for (const file of files) {
12145
- const filepath = path10.join(dirPath, file);
12402
+ const filepath = path11.join(dirPath, file);
12146
12403
  const result = await importSessionFromFile(filepath, targetProjectPath);
12147
12404
  results.push(result);
12148
12405
  if (result.success) {
@@ -12162,7 +12419,7 @@ function restoreFromArchive(archivePath, targetDir) {
12162
12419
  if (!fs9.existsSync(extractTo)) {
12163
12420
  fs9.mkdirSync(extractTo, { recursive: true });
12164
12421
  }
12165
- execSync5(`tar -xzf "${archivePath}" -C "${extractTo}"`, {
12422
+ execSync3(`tar -xzf "${archivePath}" -C "${extractTo}"`, {
12166
12423
  stdio: "ignore"
12167
12424
  });
12168
12425
  return true;
@@ -12220,7 +12477,7 @@ var importCommand = new Command25("import").description("Sessions importieren").
12220
12477
  console.log(colors.orange.bold("SHIVA Code - Import"));
12221
12478
  console.log(colors.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
12222
12479
  log.newline();
12223
- const absolutePath = path11.resolve(inputPath);
12480
+ const absolutePath = path12.resolve(inputPath);
12224
12481
  if (!fs10.existsSync(absolutePath)) {
12225
12482
  log.error(`Datei nicht gefunden: ${absolutePath}`);
12226
12483
  return;
@@ -12243,7 +12500,7 @@ var importCommand = new Command25("import").description("Sessions importieren").
12243
12500
  async function handleBackup(outputPath) {
12244
12501
  const spinner = ora17("Erstelle Backup...").start();
12245
12502
  const filename = `shiva-backup-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.tar.gz`;
12246
- const output = outputPath || path11.join(process.cwd(), filename);
12503
+ const output = outputPath || path12.join(process.cwd(), filename);
12247
12504
  const success = createBackupArchive(output);
12248
12505
  if (success) {
12249
12506
  spinner.succeed(`Backup erstellt: ${output}`);
@@ -12262,7 +12519,7 @@ async function handleExportAll(outputDir, options = {}) {
12262
12519
  return;
12263
12520
  }
12264
12521
  spinner.text = `Exportiere ${allSessionIds.length} Sessions...`;
12265
- const output = outputDir || path11.join(process.cwd(), "shiva-export");
12522
+ const output = outputDir || path12.join(process.cwd(), "shiva-export");
12266
12523
  const result = await exportSessions(allSessionIds, output, options);
12267
12524
  spinner.succeed(`Export abgeschlossen`);
12268
12525
  log.info(`${result.success} Sessions exportiert`);
@@ -12286,7 +12543,7 @@ async function handleExportProject(projectName, outputDir, options = {}) {
12286
12543
  return;
12287
12544
  }
12288
12545
  spinner.text = `Exportiere ${project.sessions.length} Sessions aus ${project.projectName}...`;
12289
- const output = outputDir || path11.join(process.cwd(), `${project.projectName}-export`);
12546
+ const output = outputDir || path12.join(process.cwd(), `${project.projectName}-export`);
12290
12547
  const result = await exportProjectSessions(project.absolutePath, output, options);
12291
12548
  spinner.succeed(`Export abgeschlossen`);
12292
12549
  log.info(`${result.success} Sessions exportiert`);
@@ -12408,20 +12665,20 @@ async function handlePreview(filepath) {
12408
12665
 
12409
12666
  // src/commands/system/doctor.ts
12410
12667
  import { Command as Command26 } from "commander";
12411
- import { execSync as execSync6 } from "child_process";
12668
+ import { execSync as execSync4 } from "child_process";
12412
12669
  import * as fs11 from "fs";
12413
- import * as path12 from "path";
12414
- import * as os5 from "os";
12670
+ import * as path13 from "path";
12671
+ import * as os6 from "os";
12415
12672
  function tryExec(command) {
12416
12673
  try {
12417
- return execSync6(command, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12674
+ return execSync4(command, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12418
12675
  } catch {
12419
12676
  return null;
12420
12677
  }
12421
12678
  }
12422
12679
  function commandExists3(cmd) {
12423
12680
  try {
12424
- execSync6(`which ${cmd}`, { stdio: ["pipe", "pipe", "pipe"] });
12681
+ execSync4(`which ${cmd}`, { stdio: ["pipe", "pipe", "pipe"] });
12425
12682
  return true;
12426
12683
  } catch {
12427
12684
  return false;
@@ -12572,8 +12829,8 @@ function checkNpm() {
12572
12829
  };
12573
12830
  }
12574
12831
  function checkShivaConfig() {
12575
- const configDir = path12.join(os5.homedir(), ".config", "shiva-code");
12576
- const configFile = path12.join(configDir, "config.json");
12832
+ const configDir = path13.join(os6.homedir(), ".config", "shiva-code");
12833
+ const configFile = path13.join(configDir, "config.json");
12577
12834
  if (!fs11.existsSync(configDir)) {
12578
12835
  return {
12579
12836
  name: "SHIVA Config",
@@ -12597,7 +12854,7 @@ function checkShivaConfig() {
12597
12854
  };
12598
12855
  }
12599
12856
  function checkClaudeProjects() {
12600
- const claudeDir = path12.join(os5.homedir(), ".claude", "projects");
12857
+ const claudeDir = path13.join(os6.homedir(), ".claude", "projects");
12601
12858
  if (!fs11.existsSync(claudeDir)) {
12602
12859
  return {
12603
12860
  name: "Claude Projects",
@@ -12608,7 +12865,7 @@ function checkClaudeProjects() {
12608
12865
  }
12609
12866
  try {
12610
12867
  const projects = fs11.readdirSync(claudeDir).filter(
12611
- (f) => fs11.statSync(path12.join(claudeDir, f)).isDirectory()
12868
+ (f) => fs11.statSync(path13.join(claudeDir, f)).isDirectory()
12612
12869
  );
12613
12870
  return {
12614
12871
  name: "Claude Projects",
@@ -12641,11 +12898,11 @@ function checkTmux() {
12641
12898
  };
12642
12899
  }
12643
12900
  function getSystemInfo() {
12644
- const totalMem = os5.totalmem();
12901
+ const totalMem = os6.totalmem();
12645
12902
  const memGB = (totalMem / (1024 * 1024 * 1024)).toFixed(1);
12646
12903
  return {
12647
- os: `${os5.type()} ${os5.release()}`,
12648
- arch: os5.arch(),
12904
+ os: `${os6.type()} ${os6.release()}`,
12905
+ arch: os6.arch(),
12649
12906
  memory: `${memGB} GB`
12650
12907
  };
12651
12908
  }
@@ -12735,16 +12992,16 @@ var doctorCommand = new Command26("doctor").description("System-Check f\xFCr SHI
12735
12992
 
12736
12993
  // src/commands/system/upgrade.ts
12737
12994
  import { Command as Command27 } from "commander";
12738
- import { execSync as execSync7, spawn as spawn6 } from "child_process";
12995
+ import { execSync as execSync5, spawn as spawn6 } from "child_process";
12739
12996
  import * as fs12 from "fs";
12740
- import * as path13 from "path";
12997
+ import * as path14 from "path";
12741
12998
  import { fileURLToPath } from "url";
12742
12999
  var __filename = fileURLToPath(import.meta.url);
12743
- var __dirname = path13.dirname(__filename);
13000
+ var __dirname = path14.dirname(__filename);
12744
13001
  var PACKAGE_NAME = "shiva-code";
12745
13002
  function getCurrentVersion() {
12746
13003
  try {
12747
- const packageJsonPath = path13.resolve(__dirname, "../../package.json");
13004
+ const packageJsonPath = path14.resolve(__dirname, "../../package.json");
12748
13005
  if (fs12.existsSync(packageJsonPath)) {
12749
13006
  const packageJson = JSON.parse(fs12.readFileSync(packageJsonPath, "utf-8"));
12750
13007
  return packageJson.version || "0.0.0";
@@ -12761,7 +13018,7 @@ async function getLatestVersion() {
12761
13018
  return data["dist-tags"]?.latest || null;
12762
13019
  } catch {
12763
13020
  try {
12764
- const output = execSync7(`npm view ${PACKAGE_NAME} version 2>/dev/null`, {
13021
+ const output = execSync5(`npm view ${PACKAGE_NAME} version 2>/dev/null`, {
12765
13022
  encoding: "utf-8"
12766
13023
  }).trim();
12767
13024
  return output || null;
@@ -12792,17 +13049,17 @@ async function checkForUpdates() {
12792
13049
  }
12793
13050
  function detectPackageManager2() {
12794
13051
  try {
12795
- const npmGlobal = execSync7("npm list -g --depth=0 2>/dev/null", { encoding: "utf-8" });
13052
+ const npmGlobal = execSync5("npm list -g --depth=0 2>/dev/null", { encoding: "utf-8" });
12796
13053
  if (npmGlobal.includes(PACKAGE_NAME)) return "npm";
12797
13054
  } catch {
12798
13055
  }
12799
13056
  try {
12800
- const yarnGlobal = execSync7("yarn global list 2>/dev/null", { encoding: "utf-8" });
13057
+ const yarnGlobal = execSync5("yarn global list 2>/dev/null", { encoding: "utf-8" });
12801
13058
  if (yarnGlobal.includes(PACKAGE_NAME)) return "yarn";
12802
13059
  } catch {
12803
13060
  }
12804
13061
  try {
12805
- const pnpmGlobal = execSync7("pnpm list -g 2>/dev/null", { encoding: "utf-8" });
13062
+ const pnpmGlobal = execSync5("pnpm list -g 2>/dev/null", { encoding: "utf-8" });
12806
13063
  if (pnpmGlobal.includes(PACKAGE_NAME)) return "pnpm";
12807
13064
  } catch {
12808
13065
  }
@@ -13144,9 +13401,9 @@ complete -F _shiva_completions shiva
13144
13401
  }
13145
13402
  async function installBashCompletion() {
13146
13403
  const fs15 = await import("fs");
13147
- const os7 = await import("os");
13148
- const path15 = await import("path");
13149
- const bashrcPath = path15.join(os7.homedir(), ".bashrc");
13404
+ const os8 = await import("os");
13405
+ const path16 = await import("path");
13406
+ const bashrcPath = path16.join(os8.homedir(), ".bashrc");
13150
13407
  const completionScript = generateBashCompletion();
13151
13408
  const marker = "# SHIVA Code Bash Completion";
13152
13409
  try {
@@ -13287,9 +13544,9 @@ compdef _shiva shiva
13287
13544
  }
13288
13545
  async function installZshCompletion() {
13289
13546
  const fs15 = await import("fs");
13290
- const os7 = await import("os");
13291
- const path15 = await import("path");
13292
- const zshrcPath = path15.join(os7.homedir(), ".zshrc");
13547
+ const os8 = await import("os");
13548
+ const path16 = await import("path");
13549
+ const zshrcPath = path16.join(os8.homedir(), ".zshrc");
13293
13550
  const completionScript = generateZshCompletion();
13294
13551
  const marker = "# SHIVA Code Zsh Completion";
13295
13552
  try {
@@ -13392,10 +13649,10 @@ complete -c shiva -n "__fish_seen_subcommand_from stats" -l json -d "JSON Output
13392
13649
  }
13393
13650
  async function installFishCompletion() {
13394
13651
  const fs15 = await import("fs");
13395
- const os7 = await import("os");
13396
- const path15 = await import("path");
13397
- const fishCompletionsDir = path15.join(os7.homedir(), ".config", "fish", "completions");
13398
- const fishCompletionPath = path15.join(fishCompletionsDir, "shiva.fish");
13652
+ const os8 = await import("os");
13653
+ const path16 = await import("path");
13654
+ const fishCompletionsDir = path16.join(os8.homedir(), ".config", "fish", "completions");
13655
+ const fishCompletionPath = path16.join(fishCompletionsDir, "shiva.fish");
13399
13656
  const completionScript = generateFishCompletion();
13400
13657
  try {
13401
13658
  if (!fs15.existsSync(fishCompletionsDir)) {
@@ -13803,8 +14060,8 @@ dockerCommand.action(() => {
13803
14060
  // src/commands/advanced/workflow.ts
13804
14061
  import { Command as Command32 } from "commander";
13805
14062
  import * as fs13 from "fs";
13806
- import * as path14 from "path";
13807
- import * as os6 from "os";
14063
+ import * as path15 from "path";
14064
+ import * as os7 from "os";
13808
14065
  import ora20 from "ora";
13809
14066
  import inquirer12 from "inquirer";
13810
14067
  var builtInWorkflows = {
@@ -13963,7 +14220,7 @@ workflowCommand.command("delete").alias("rm").description("Workflow l\xF6schen")
13963
14220
  log.success(`Workflow "${name}" gel\xF6scht`);
13964
14221
  });
13965
14222
  function getWorkflowsPath() {
13966
- return path14.join(os6.homedir(), ".shiva", "workflows.json");
14223
+ return path15.join(os7.homedir(), ".shiva", "workflows.json");
13967
14224
  }
13968
14225
  function loadCustomWorkflows() {
13969
14226
  const filepath = getWorkflowsPath();
@@ -13979,7 +14236,7 @@ function loadCustomWorkflows() {
13979
14236
  }
13980
14237
  function saveCustomWorkflow(name, workflow) {
13981
14238
  const filepath = getWorkflowsPath();
13982
- const dir = path14.dirname(filepath);
14239
+ const dir = path15.dirname(filepath);
13983
14240
  if (!fs13.existsSync(dir)) {
13984
14241
  fs13.mkdirSync(dir, { recursive: true });
13985
14242
  }
@@ -14082,9 +14339,9 @@ async function executeStep(step) {
14082
14339
  // src/commands/advanced/hook.ts
14083
14340
  import { Command as Command33 } from "commander";
14084
14341
  import { existsSync as existsSync21, readFileSync as readFileSync10, writeFileSync as writeFileSync10, mkdirSync as mkdirSync6 } from "fs";
14085
- import { homedir as homedir7 } from "os";
14086
- import { join as join12 } from "path";
14087
- var CLAUDE_SETTINGS_PATH = join12(homedir7(), ".claude", "settings.json");
14342
+ import { homedir as homedir8 } from "os";
14343
+ import { join as join13 } from "path";
14344
+ var CLAUDE_SETTINGS_PATH = join13(homedir8(), ".claude", "settings.json");
14088
14345
  function getClaudeSettings() {
14089
14346
  if (!existsSync21(CLAUDE_SETTINGS_PATH)) {
14090
14347
  return {};
@@ -14097,7 +14354,7 @@ function getClaudeSettings() {
14097
14354
  }
14098
14355
  }
14099
14356
  function saveClaudeSettings(settings) {
14100
- const dir = join12(homedir7(), ".claude");
14357
+ const dir = join13(homedir8(), ".claude");
14101
14358
  if (!existsSync21(dir)) {
14102
14359
  mkdirSync6(dir, { recursive: true });
14103
14360
  }
@@ -14118,7 +14375,7 @@ function removeShivaHooks(eventHooks) {
14118
14375
  var hookCommand = new Command33("hook").description("Claude Code Hook Integration verwalten");
14119
14376
  hookCommand.command("install").description("SHIVA Hooks in Claude Code installieren").option("--github", "GitHub Context Injection aktivieren").option("--sync", "Cloud Sync Hooks aktivieren (Standard)").option("--scan", "Package Security Scanning aktivieren").option("--all", "Alle Hooks aktivieren").action((options) => {
14120
14377
  log.brand();
14121
- const claudePath = join12(homedir7(), ".claude");
14378
+ const claudePath = join13(homedir8(), ".claude");
14122
14379
  if (!existsSync21(claudePath)) {
14123
14380
  log.error("Claude Code nicht gefunden");
14124
14381
  log.newline();
@@ -14387,7 +14644,7 @@ hookCommand.command("branch-switch").description("Branch-Wechsel behandeln (f\xF
14387
14644
  }
14388
14645
  const { getCurrentBranch: getCurrentBranch2 } = await import("./api-OEHQTBH7.js");
14389
14646
  const { getSessionForBranch: getSessionForBranch2, hasShivaDir: hasShivaDir2 } = await import("./config-D6M6LI6U.js");
14390
- const { findProject: findProject3, findSessionByBranch: findSessionByBranch2, formatRelativeTime: formatRelativeTime3 } = await import("./manager-LXNF7QWT.js");
14647
+ const { findProject: findProject3, findSessionByBranch: findSessionByBranch2, formatRelativeTime: formatRelativeTime3 } = await import("./manager-ZPQWG7E6.js");
14391
14648
  const projectPath = process.cwd();
14392
14649
  const newBranch = getCurrentBranch2(projectPath);
14393
14650
  let sessionInfo = null;
@@ -14639,7 +14896,7 @@ function listPackages() {
14639
14896
 
14640
14897
  // src/index.ts
14641
14898
  var program = new Command35();
14642
- program.name("shiva").description("SHIVA Code - Control Station for Claude Code").version("0.5.0");
14899
+ program.name("shiva").description("SHIVA Code - Control Station for Claude Code").version("0.5.1");
14643
14900
  program.addCommand(loginCommand);
14644
14901
  program.addCommand(logoutCommand);
14645
14902
  program.addCommand(sessionsCommand);
@@ -14852,7 +15109,7 @@ async function showDashboard() {
14852
15109
  const packageName = selected.data;
14853
15110
  log.newline();
14854
15111
  log.success(`Starte Package ${packageName}...`);
14855
- const { getPackageLaunchConfig: getPackageLaunchConfig2 } = await import("./package-manager-WF3UW2J4.js");
15112
+ const { getPackageLaunchConfig: getPackageLaunchConfig2 } = await import("./package-manager-NHNQATBH.js");
14856
15113
  const launches = await getPackageLaunchConfig2(packageName);
14857
15114
  if (launches.length > 0) {
14858
15115
  await spawnProjects(launches, detectTerminal());