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/{chunk-IDM6KY2R.js → chunk-H5OFO4VS.js} +115 -22
- package/dist/{chunk-5RCSFT5F.js → chunk-LBTCSQAX.js} +1 -1
- package/dist/index.js +627 -370
- package/dist/{manager-LXNF7QWT.js → manager-ZPQWG7E6.js} +1 -1
- package/dist/{package-manager-WF3UW2J4.js → package-manager-NHNQATBH.js} +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
getPackageLaunchConfig,
|
|
50
50
|
getPackageStats,
|
|
51
51
|
removeProjectFromPackage
|
|
52
|
-
} from "./chunk-
|
|
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-
|
|
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(
|
|
317
|
+
async findProjectByPath(path16) {
|
|
314
318
|
const projects = await this.getProjects();
|
|
315
|
-
return projects.find((p) => p.path ===
|
|
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
|
|
896
|
+
const os8 = __require("os");
|
|
893
897
|
const components = [
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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
|
|
905
|
-
const hostname =
|
|
906
|
-
const 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: (
|
|
2408
|
+
PATH_NOT_FOUND: (path16) => new ShivaError(
|
|
2405
2409
|
"PATH_404",
|
|
2406
|
-
|
|
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: (
|
|
2418
|
+
PERMISSION_DENIED: (path16) => new ShivaError(
|
|
2415
2419
|
"PERMISSION_DENIED",
|
|
2416
|
-
|
|
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
|
|
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,
|
|
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 {
|
|
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
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
socket
|
|
4438
|
-
|
|
4439
|
-
|
|
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
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
socket
|
|
4450
|
-
|
|
4451
|
-
|
|
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
|
-
|
|
4519
|
-
|
|
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
|
|
4538
|
-
|
|
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
|
-
|
|
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
|
|
4581
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4745
|
+
const args = ["logs"];
|
|
4746
|
+
if (tail && tail > 0) {
|
|
4747
|
+
args.push("--tail", String(tail));
|
|
4748
|
+
}
|
|
4749
|
+
args.push(containerId);
|
|
4625
4750
|
try {
|
|
4626
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4981
|
-
|
|
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
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
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-
|
|
5263
|
+
"new-session",
|
|
5264
|
+
"-d",
|
|
5265
|
+
"-s",
|
|
5266
|
+
tmuxSession,
|
|
5038
5267
|
"-n",
|
|
5039
|
-
|
|
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
|
-
|
|
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}:${
|
|
5099
|
-
|
|
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 (
|
|
5111
|
-
const
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
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
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
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
|
-
|
|
5344
|
+
safeName,
|
|
5134
5345
|
"--cwd",
|
|
5135
5346
|
project.projectPath,
|
|
5136
|
-
"
|
|
5137
|
-
|
|
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
|
|
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
|
-
|
|
5391
|
+
safeName,
|
|
5179
5392
|
"--working-directory",
|
|
5180
5393
|
project.projectPath,
|
|
5181
5394
|
"-e",
|
|
5182
|
-
"
|
|
5183
|
-
|
|
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
|
|
5194
|
-
|
|
5195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
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
|
|
5228
|
-
const
|
|
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
|
-
|
|
5459
|
+
safeName
|
|
5233
5460
|
], { stdio: "inherit" });
|
|
5234
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
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
|
|
5489
|
+
function buildSecureDockerArgs(project, settings, containerName) {
|
|
5258
5490
|
const cmd = dockerService.getDockerInfo().runtime === "podman" ? "podman" : "docker";
|
|
5259
|
-
const
|
|
5260
|
-
const
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
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
|
-
|
|
5514
|
+
if (/^[A-Z_][A-Z0-9_]*$/.test(key)) {
|
|
5515
|
+
args.push("-e", `${key}=${value}`);
|
|
5516
|
+
}
|
|
5270
5517
|
}
|
|
5271
|
-
|
|
5518
|
+
args.push(settings.defaultImage, "claude");
|
|
5272
5519
|
if (project.sessionId && !project.newSession) {
|
|
5273
|
-
|
|
5520
|
+
args.push("--resume", project.sessionId);
|
|
5274
5521
|
}
|
|
5275
|
-
return
|
|
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
|
|
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
|
|
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(
|
|
5614
|
+
isGitRepo(path16) {
|
|
5365
5615
|
try {
|
|
5366
|
-
|
|
5367
|
-
cwd:
|
|
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
|
-
|
|
5630
|
+
execSync("docker info", { stdio: "pipe" });
|
|
5381
5631
|
return true;
|
|
5382
5632
|
} catch {
|
|
5383
5633
|
try {
|
|
5384
|
-
|
|
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
|
|
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 =
|
|
5719
|
+
const sandboxPath = join4(sandboxDir, `session-${sessionId}`);
|
|
5470
5720
|
mkdirSync(dirname(sandboxPath), { recursive: true });
|
|
5471
|
-
const currentRef =
|
|
5721
|
+
const currentRef = execSync("git rev-parse HEAD", {
|
|
5472
5722
|
cwd: projectPath,
|
|
5473
5723
|
encoding: "utf-8"
|
|
5474
5724
|
}).trim();
|
|
5475
|
-
|
|
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 =
|
|
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 =
|
|
5494
|
-
const destPath =
|
|
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 =
|
|
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 =
|
|
5526
|
-
const destPath =
|
|
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 =
|
|
5547
|
-
const upperDir =
|
|
5548
|
-
const workDir =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
5903
|
+
const diffOutput = execSync("git diff --stat --numstat HEAD", {
|
|
5654
5904
|
cwd: session.sandboxPath,
|
|
5655
5905
|
encoding: "utf-8"
|
|
5656
5906
|
});
|
|
5657
|
-
const untrackedOutput =
|
|
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 =
|
|
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 =
|
|
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 ?
|
|
5720
|
-
const originalEntryPath =
|
|
5721
|
-
const sandboxEntryPath =
|
|
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 =
|
|
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 ?
|
|
5764
|
-
const originalEntryPath =
|
|
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 =
|
|
5808
|
-
const entryPath =
|
|
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 =
|
|
5832
|
-
const entryPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
5873
|
-
const sandboxFilePath =
|
|
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 =
|
|
5932
|
-
const destPath =
|
|
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 =
|
|
5970
|
-
const originalPath =
|
|
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 =
|
|
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
|
|
6818
|
-
import * as
|
|
7067
|
+
import * as path5 from "path";
|
|
7068
|
+
import * as os4 from "os";
|
|
6819
7069
|
function getTagsPath() {
|
|
6820
|
-
return
|
|
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 =
|
|
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(
|
|
7338
|
+
async function checkProjectExists(path16) {
|
|
7089
7339
|
try {
|
|
7090
7340
|
const fs15 = await import("fs");
|
|
7091
|
-
return fs15.existsSync(
|
|
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
|
|
7353
|
-
const change = diff.changes.find((c) => c.path ===
|
|
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)} ${
|
|
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
|
|
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 =
|
|
7973
|
-
const hooksDir =
|
|
7974
|
-
const hookFile =
|
|
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-
|
|
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-
|
|
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
|
|
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(
|
|
9966
|
+
if (existsSync12(join7(dir, "pnpm-lock.yaml"))) {
|
|
9717
9967
|
manager = "pnpm";
|
|
9718
|
-
} else if (existsSync12(
|
|
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(
|
|
9820
|
-
const parsed = await parsePackageJson(
|
|
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(
|
|
9824
|
-
packagesToScan = await parseRequirementsTxt(
|
|
10073
|
+
} else if (existsSync12(join7(cwd, "requirements.txt"))) {
|
|
10074
|
+
packagesToScan = await parseRequirementsTxt(join7(cwd, "requirements.txt"));
|
|
9825
10075
|
manager = "pip";
|
|
9826
|
-
} else if (existsSync12(
|
|
9827
|
-
packagesToScan = await parseCargoToml(
|
|
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
|
|
10781
|
+
import * as path7 from "path";
|
|
10532
10782
|
|
|
10533
10783
|
// src/utils/clipboard.ts
|
|
10534
|
-
import { execSync as
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 ${
|
|
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",
|
|
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
|
|
10980
|
-
import * as
|
|
11236
|
+
import * as path8 from "path";
|
|
11237
|
+
import * as os5 from "os";
|
|
10981
11238
|
function findAllClaudeMdFiles() {
|
|
10982
11239
|
const results = [];
|
|
10983
|
-
const sessionsPath =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
11186
|
-
const claudeMdPath =
|
|
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 =
|
|
11209
|
-
const contextPath =
|
|
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
|
|
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 ?
|
|
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 =
|
|
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: ${
|
|
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
|
|
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 ?
|
|
11553
|
-
const projectName =
|
|
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
|
|
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
|
|
11945
|
-
import { execSync as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
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
|
|
12668
|
+
import { execSync as execSync4 } from "child_process";
|
|
12412
12669
|
import * as fs11 from "fs";
|
|
12413
|
-
import * as
|
|
12414
|
-
import * as
|
|
12670
|
+
import * as path13 from "path";
|
|
12671
|
+
import * as os6 from "os";
|
|
12415
12672
|
function tryExec(command) {
|
|
12416
12673
|
try {
|
|
12417
|
-
return
|
|
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
|
-
|
|
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 =
|
|
12576
|
-
const configFile =
|
|
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 =
|
|
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(
|
|
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 =
|
|
12901
|
+
const totalMem = os6.totalmem();
|
|
12645
12902
|
const memGB = (totalMem / (1024 * 1024 * 1024)).toFixed(1);
|
|
12646
12903
|
return {
|
|
12647
|
-
os: `${
|
|
12648
|
-
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
|
|
12995
|
+
import { execSync as execSync5, spawn as spawn6 } from "child_process";
|
|
12739
12996
|
import * as fs12 from "fs";
|
|
12740
|
-
import * as
|
|
12997
|
+
import * as path14 from "path";
|
|
12741
12998
|
import { fileURLToPath } from "url";
|
|
12742
12999
|
var __filename = fileURLToPath(import.meta.url);
|
|
12743
|
-
var __dirname =
|
|
13000
|
+
var __dirname = path14.dirname(__filename);
|
|
12744
13001
|
var PACKAGE_NAME = "shiva-code";
|
|
12745
13002
|
function getCurrentVersion() {
|
|
12746
13003
|
try {
|
|
12747
|
-
const packageJsonPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
13148
|
-
const
|
|
13149
|
-
const bashrcPath =
|
|
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
|
|
13291
|
-
const
|
|
13292
|
-
const zshrcPath =
|
|
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
|
|
13396
|
-
const
|
|
13397
|
-
const fishCompletionsDir =
|
|
13398
|
-
const fishCompletionPath =
|
|
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
|
|
13807
|
-
import * as
|
|
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
|
|
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 =
|
|
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
|
|
14086
|
-
import { join as
|
|
14087
|
-
var CLAUDE_SETTINGS_PATH =
|
|
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 =
|
|
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 =
|
|
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-
|
|
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.
|
|
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-
|
|
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());
|