episoda 0.2.106 → 0.2.108
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/daemon/daemon-process.js +382 -216
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +28 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1571,15 +1571,15 @@ var require_git_executor = __commonJS({
|
|
|
1571
1571
|
try {
|
|
1572
1572
|
const { stdout: gitDir } = await execAsync3("git rev-parse --git-dir", { cwd, timeout: 5e3 });
|
|
1573
1573
|
const gitDirPath = gitDir.trim();
|
|
1574
|
-
const
|
|
1574
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1575
1575
|
const rebaseMergePath = `${gitDirPath}/rebase-merge`;
|
|
1576
1576
|
const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
|
|
1577
1577
|
try {
|
|
1578
|
-
await
|
|
1578
|
+
await fs24.access(rebaseMergePath);
|
|
1579
1579
|
inRebase = true;
|
|
1580
1580
|
} catch {
|
|
1581
1581
|
try {
|
|
1582
|
-
await
|
|
1582
|
+
await fs24.access(rebaseApplyPath);
|
|
1583
1583
|
inRebase = true;
|
|
1584
1584
|
} catch {
|
|
1585
1585
|
inRebase = false;
|
|
@@ -1626,16 +1626,18 @@ var require_git_executor = __commonJS({
|
|
|
1626
1626
|
*/
|
|
1627
1627
|
async executeWorktreeAdd(command, cwd, options) {
|
|
1628
1628
|
try {
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1629
|
+
if (!command.detach && command.branch) {
|
|
1630
|
+
const validation = (0, git_validator_1.validateBranchName)(command.branch);
|
|
1631
|
+
if (!validation.valid) {
|
|
1632
|
+
return {
|
|
1633
|
+
success: false,
|
|
1634
|
+
error: validation.error || "UNKNOWN_ERROR"
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1635
1637
|
}
|
|
1636
|
-
const
|
|
1638
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1637
1639
|
try {
|
|
1638
|
-
await
|
|
1640
|
+
await fs24.access(command.path);
|
|
1639
1641
|
return {
|
|
1640
1642
|
success: false,
|
|
1641
1643
|
error: "WORKTREE_EXISTS",
|
|
@@ -1648,7 +1650,14 @@ var require_git_executor = __commonJS({
|
|
|
1648
1650
|
} catch {
|
|
1649
1651
|
}
|
|
1650
1652
|
const args = ["worktree", "add"];
|
|
1651
|
-
if (command.
|
|
1653
|
+
if (command.detach) {
|
|
1654
|
+
args.push("--detach", command.path);
|
|
1655
|
+
if (command.startPoint) {
|
|
1656
|
+
args.push(command.startPoint);
|
|
1657
|
+
} else {
|
|
1658
|
+
args.push("HEAD");
|
|
1659
|
+
}
|
|
1660
|
+
} else if (command.create) {
|
|
1652
1661
|
args.push("-b", command.branch, command.path);
|
|
1653
1662
|
if (command.startPoint) {
|
|
1654
1663
|
args.push(command.startPoint);
|
|
@@ -1658,12 +1667,14 @@ var require_git_executor = __commonJS({
|
|
|
1658
1667
|
}
|
|
1659
1668
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1660
1669
|
if (result.success) {
|
|
1670
|
+
const outputMsg = command.detach ? `Created detached worktree at ${command.path} from ${command.startPoint || "HEAD"}` : `Created worktree at ${command.path} for branch ${command.branch}`;
|
|
1661
1671
|
return {
|
|
1662
1672
|
success: true,
|
|
1663
|
-
output:
|
|
1673
|
+
output: outputMsg,
|
|
1664
1674
|
details: {
|
|
1665
1675
|
worktreePath: command.path,
|
|
1666
|
-
branchName: command.branch
|
|
1676
|
+
branchName: command.detach ? void 0 : command.branch,
|
|
1677
|
+
detached: command.detach
|
|
1667
1678
|
}
|
|
1668
1679
|
};
|
|
1669
1680
|
}
|
|
@@ -1689,9 +1700,9 @@ var require_git_executor = __commonJS({
|
|
|
1689
1700
|
*/
|
|
1690
1701
|
async executeWorktreeRemove(command, cwd, options) {
|
|
1691
1702
|
try {
|
|
1692
|
-
const
|
|
1703
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1693
1704
|
try {
|
|
1694
|
-
await
|
|
1705
|
+
await fs24.access(command.path);
|
|
1695
1706
|
} catch {
|
|
1696
1707
|
return {
|
|
1697
1708
|
success: false,
|
|
@@ -1726,7 +1737,7 @@ var require_git_executor = __commonJS({
|
|
|
1726
1737
|
const result = await this.runGitCommand(args, cwd, options);
|
|
1727
1738
|
if (result.success) {
|
|
1728
1739
|
try {
|
|
1729
|
-
await
|
|
1740
|
+
await fs24.rm(command.path, { recursive: true, force: true });
|
|
1730
1741
|
} catch {
|
|
1731
1742
|
}
|
|
1732
1743
|
return {
|
|
@@ -1860,10 +1871,10 @@ var require_git_executor = __commonJS({
|
|
|
1860
1871
|
*/
|
|
1861
1872
|
async executeCloneBare(command, options) {
|
|
1862
1873
|
try {
|
|
1863
|
-
const
|
|
1864
|
-
const
|
|
1874
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1875
|
+
const path25 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1865
1876
|
try {
|
|
1866
|
-
await
|
|
1877
|
+
await fs24.access(command.path);
|
|
1867
1878
|
return {
|
|
1868
1879
|
success: false,
|
|
1869
1880
|
error: "BRANCH_ALREADY_EXISTS",
|
|
@@ -1872,9 +1883,9 @@ var require_git_executor = __commonJS({
|
|
|
1872
1883
|
};
|
|
1873
1884
|
} catch {
|
|
1874
1885
|
}
|
|
1875
|
-
const parentDir =
|
|
1886
|
+
const parentDir = path25.dirname(command.path);
|
|
1876
1887
|
try {
|
|
1877
|
-
await
|
|
1888
|
+
await fs24.mkdir(parentDir, { recursive: true });
|
|
1878
1889
|
} catch {
|
|
1879
1890
|
}
|
|
1880
1891
|
const { stdout, stderr } = await execAsync3(
|
|
@@ -1922,22 +1933,22 @@ var require_git_executor = __commonJS({
|
|
|
1922
1933
|
*/
|
|
1923
1934
|
async executeProjectInfo(cwd, options) {
|
|
1924
1935
|
try {
|
|
1925
|
-
const
|
|
1926
|
-
const
|
|
1936
|
+
const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
|
|
1937
|
+
const path25 = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1927
1938
|
let currentPath = cwd;
|
|
1928
1939
|
let projectPath = cwd;
|
|
1929
1940
|
let bareRepoPath;
|
|
1930
1941
|
for (let i = 0; i < 10; i++) {
|
|
1931
|
-
const bareDir =
|
|
1932
|
-
const episodaDir =
|
|
1942
|
+
const bareDir = path25.join(currentPath, ".bare");
|
|
1943
|
+
const episodaDir = path25.join(currentPath, ".episoda");
|
|
1933
1944
|
try {
|
|
1934
|
-
await
|
|
1935
|
-
await
|
|
1945
|
+
await fs24.access(bareDir);
|
|
1946
|
+
await fs24.access(episodaDir);
|
|
1936
1947
|
projectPath = currentPath;
|
|
1937
1948
|
bareRepoPath = bareDir;
|
|
1938
1949
|
break;
|
|
1939
1950
|
} catch {
|
|
1940
|
-
const parentPath =
|
|
1951
|
+
const parentPath = path25.dirname(currentPath);
|
|
1941
1952
|
if (parentPath === currentPath) {
|
|
1942
1953
|
break;
|
|
1943
1954
|
}
|
|
@@ -2590,31 +2601,31 @@ var require_auth = __commonJS({
|
|
|
2590
2601
|
exports2.loadConfig = loadConfig8;
|
|
2591
2602
|
exports2.saveConfig = saveConfig2;
|
|
2592
2603
|
exports2.validateToken = validateToken;
|
|
2593
|
-
var
|
|
2594
|
-
var
|
|
2604
|
+
var fs24 = __importStar(require("fs"));
|
|
2605
|
+
var path25 = __importStar(require("path"));
|
|
2595
2606
|
var os9 = __importStar(require("os"));
|
|
2596
2607
|
var child_process_1 = require("child_process");
|
|
2597
2608
|
var DEFAULT_CONFIG_FILE = "config.json";
|
|
2598
2609
|
function getConfigDir8() {
|
|
2599
|
-
return process.env.EPISODA_CONFIG_DIR ||
|
|
2610
|
+
return process.env.EPISODA_CONFIG_DIR || path25.join(os9.homedir(), ".episoda");
|
|
2600
2611
|
}
|
|
2601
2612
|
function getConfigPath(configPath) {
|
|
2602
2613
|
if (configPath) {
|
|
2603
2614
|
return configPath;
|
|
2604
2615
|
}
|
|
2605
|
-
return
|
|
2616
|
+
return path25.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
|
|
2606
2617
|
}
|
|
2607
2618
|
function ensureConfigDir(configPath) {
|
|
2608
|
-
const dir =
|
|
2609
|
-
const isNew = !
|
|
2619
|
+
const dir = path25.dirname(configPath);
|
|
2620
|
+
const isNew = !fs24.existsSync(dir);
|
|
2610
2621
|
if (isNew) {
|
|
2611
|
-
|
|
2622
|
+
fs24.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2612
2623
|
}
|
|
2613
2624
|
if (process.platform === "darwin") {
|
|
2614
|
-
const nosyncPath =
|
|
2615
|
-
if (isNew || !
|
|
2625
|
+
const nosyncPath = path25.join(dir, ".nosync");
|
|
2626
|
+
if (isNew || !fs24.existsSync(nosyncPath)) {
|
|
2616
2627
|
try {
|
|
2617
|
-
|
|
2628
|
+
fs24.writeFileSync(nosyncPath, "", { mode: 384 });
|
|
2618
2629
|
(0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
|
|
2619
2630
|
stdio: "ignore",
|
|
2620
2631
|
timeout: 5e3
|
|
@@ -2626,9 +2637,9 @@ var require_auth = __commonJS({
|
|
|
2626
2637
|
}
|
|
2627
2638
|
async function loadConfig8(configPath) {
|
|
2628
2639
|
const fullPath = getConfigPath(configPath);
|
|
2629
|
-
if (
|
|
2640
|
+
if (fs24.existsSync(fullPath)) {
|
|
2630
2641
|
try {
|
|
2631
|
-
const content =
|
|
2642
|
+
const content = fs24.readFileSync(fullPath, "utf8");
|
|
2632
2643
|
const config = JSON.parse(content);
|
|
2633
2644
|
return config;
|
|
2634
2645
|
} catch (error) {
|
|
@@ -2638,9 +2649,9 @@ var require_auth = __commonJS({
|
|
|
2638
2649
|
if (process.env.EPISODA_MODE === "cloud" && process.env.EPISODA_WORKSPACE) {
|
|
2639
2650
|
const homeDir = process.env.HOME || require("os").homedir();
|
|
2640
2651
|
const workspaceConfigPath = require("path").join(homeDir, "episoda", process.env.EPISODA_WORKSPACE, ".episoda", "config.json");
|
|
2641
|
-
if (
|
|
2652
|
+
if (fs24.existsSync(workspaceConfigPath)) {
|
|
2642
2653
|
try {
|
|
2643
|
-
const content =
|
|
2654
|
+
const content = fs24.readFileSync(workspaceConfigPath, "utf8");
|
|
2644
2655
|
const workspaceConfig = JSON.parse(content);
|
|
2645
2656
|
return {
|
|
2646
2657
|
access_token: process.env.EPISODA_ACCESS_TOKEN || workspaceConfig.accessToken,
|
|
@@ -2687,7 +2698,7 @@ var require_auth = __commonJS({
|
|
|
2687
2698
|
ensureConfigDir(fullPath);
|
|
2688
2699
|
try {
|
|
2689
2700
|
const content = JSON.stringify(config, null, 2);
|
|
2690
|
-
|
|
2701
|
+
fs24.writeFileSync(fullPath, content, { mode: 384 });
|
|
2691
2702
|
} catch (error) {
|
|
2692
2703
|
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2693
2704
|
}
|
|
@@ -2804,7 +2815,7 @@ var require_package = __commonJS({
|
|
|
2804
2815
|
"package.json"(exports2, module2) {
|
|
2805
2816
|
module2.exports = {
|
|
2806
2817
|
name: "episoda",
|
|
2807
|
-
version: "0.2.
|
|
2818
|
+
version: "0.2.107",
|
|
2808
2819
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2809
2820
|
main: "dist/index.js",
|
|
2810
2821
|
types: "dist/index.d.ts",
|
|
@@ -2843,7 +2854,7 @@ var require_package = __commonJS({
|
|
|
2843
2854
|
"@modelcontextprotocol/server-github": "^0.6.0"
|
|
2844
2855
|
},
|
|
2845
2856
|
devDependencies: {
|
|
2846
|
-
"@episoda/core": "
|
|
2857
|
+
"@episoda/core": "workspace:*",
|
|
2847
2858
|
"@types/node": "^20.11.24",
|
|
2848
2859
|
"@types/semver": "7.7.1",
|
|
2849
2860
|
"@types/tar": "6.1.13",
|
|
@@ -4058,8 +4069,8 @@ async function handleExec(command, projectPath) {
|
|
|
4058
4069
|
}
|
|
4059
4070
|
|
|
4060
4071
|
// src/daemon/handlers/worktree-handlers.ts
|
|
4061
|
-
var
|
|
4062
|
-
var
|
|
4072
|
+
var path16 = __toESM(require("path"));
|
|
4073
|
+
var fs15 = __toESM(require("fs"));
|
|
4063
4074
|
var import_child_process8 = require("child_process");
|
|
4064
4075
|
var import_util = require("util");
|
|
4065
4076
|
|
|
@@ -4191,7 +4202,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
4191
4202
|
* Create a worktree for a module
|
|
4192
4203
|
* The entire operation is locked to prevent race conditions
|
|
4193
4204
|
*/
|
|
4194
|
-
async createWorktree(moduleUid, branchName, createBranch = false) {
|
|
4205
|
+
async createWorktree(moduleUid, branchName, createBranch = false, detachedHead = false) {
|
|
4195
4206
|
if (!validateModuleUid(moduleUid)) {
|
|
4196
4207
|
return {
|
|
4197
4208
|
success: false,
|
|
@@ -4230,9 +4241,13 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
4230
4241
|
const result = await this.gitExecutor.execute({
|
|
4231
4242
|
action: "worktree_add",
|
|
4232
4243
|
path: worktreePath,
|
|
4233
|
-
branch: branchName,
|
|
4234
|
-
|
|
4235
|
-
|
|
4244
|
+
branch: detachedHead ? void 0 : branchName,
|
|
4245
|
+
// No branch for detached HEAD
|
|
4246
|
+
create: detachedHead ? false : createBranch,
|
|
4247
|
+
// Don't create branch for detached
|
|
4248
|
+
detach: detachedHead,
|
|
4249
|
+
// EP1223: Detach flag
|
|
4250
|
+
startPoint: detachedHead ? "origin/main" : createBranch ? "origin/main" : void 0
|
|
4236
4251
|
}, { cwd: this.bareRepoPath });
|
|
4237
4252
|
if (!result.success) {
|
|
4238
4253
|
return {
|
|
@@ -7332,6 +7347,144 @@ async function deleteWorktree(config, moduleUid) {
|
|
|
7332
7347
|
}
|
|
7333
7348
|
}
|
|
7334
7349
|
|
|
7350
|
+
// src/daemon/package-manager.ts
|
|
7351
|
+
var fs14 = __toESM(require("fs"));
|
|
7352
|
+
var path15 = __toESM(require("path"));
|
|
7353
|
+
var PACKAGE_MANAGERS = {
|
|
7354
|
+
javascript: [
|
|
7355
|
+
{
|
|
7356
|
+
name: "pnpm",
|
|
7357
|
+
detector: (p) => {
|
|
7358
|
+
if (fs14.existsSync(path15.join(p, "pnpm-lock.yaml"))) {
|
|
7359
|
+
return "pnpm-lock.yaml";
|
|
7360
|
+
}
|
|
7361
|
+
const pkgJsonPath = path15.join(p, "package.json");
|
|
7362
|
+
if (fs14.existsSync(pkgJsonPath)) {
|
|
7363
|
+
try {
|
|
7364
|
+
const pkg = JSON.parse(fs14.readFileSync(pkgJsonPath, "utf-8"));
|
|
7365
|
+
if (pkg.packageManager?.startsWith("pnpm")) {
|
|
7366
|
+
return "package.json (packageManager field)";
|
|
7367
|
+
}
|
|
7368
|
+
} catch {
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
return null;
|
|
7372
|
+
},
|
|
7373
|
+
config: {
|
|
7374
|
+
installCmd: "pnpm install --frozen-lockfile",
|
|
7375
|
+
cacheEnvVar: "PNPM_HOME"
|
|
7376
|
+
}
|
|
7377
|
+
},
|
|
7378
|
+
{
|
|
7379
|
+
name: "yarn",
|
|
7380
|
+
detector: (p) => fs14.existsSync(path15.join(p, "yarn.lock")) ? "yarn.lock" : null,
|
|
7381
|
+
config: {
|
|
7382
|
+
installCmd: "yarn install --frozen-lockfile",
|
|
7383
|
+
cacheEnvVar: "YARN_CACHE_FOLDER"
|
|
7384
|
+
}
|
|
7385
|
+
},
|
|
7386
|
+
{
|
|
7387
|
+
name: "bun",
|
|
7388
|
+
detector: (p) => fs14.existsSync(path15.join(p, "bun.lockb")) ? "bun.lockb" : null,
|
|
7389
|
+
config: {
|
|
7390
|
+
installCmd: "bun install --frozen-lockfile"
|
|
7391
|
+
}
|
|
7392
|
+
},
|
|
7393
|
+
{
|
|
7394
|
+
name: "npm",
|
|
7395
|
+
detector: (p) => fs14.existsSync(path15.join(p, "package-lock.json")) ? "package-lock.json" : null,
|
|
7396
|
+
config: {
|
|
7397
|
+
installCmd: "npm ci"
|
|
7398
|
+
}
|
|
7399
|
+
},
|
|
7400
|
+
{
|
|
7401
|
+
// EP1222: Default to pnpm for new projects without lockfile
|
|
7402
|
+
// This encourages standardization on pnpm across Episoda projects
|
|
7403
|
+
name: "pnpm",
|
|
7404
|
+
detector: (p) => fs14.existsSync(path15.join(p, "package.json")) ? "package.json (no lockfile - defaulting to pnpm)" : null,
|
|
7405
|
+
config: {
|
|
7406
|
+
installCmd: "pnpm install",
|
|
7407
|
+
cacheEnvVar: "PNPM_HOME"
|
|
7408
|
+
}
|
|
7409
|
+
}
|
|
7410
|
+
],
|
|
7411
|
+
python: [
|
|
7412
|
+
{
|
|
7413
|
+
name: "uv",
|
|
7414
|
+
detector: (p) => {
|
|
7415
|
+
const pyprojectPath = path15.join(p, "pyproject.toml");
|
|
7416
|
+
if (fs14.existsSync(path15.join(p, "uv.lock"))) {
|
|
7417
|
+
return "uv.lock";
|
|
7418
|
+
}
|
|
7419
|
+
if (fs14.existsSync(pyprojectPath)) {
|
|
7420
|
+
try {
|
|
7421
|
+
const content = fs14.readFileSync(pyprojectPath, "utf-8");
|
|
7422
|
+
if (content.includes("[tool.uv]") || content.includes("[project]")) {
|
|
7423
|
+
return "pyproject.toml";
|
|
7424
|
+
}
|
|
7425
|
+
} catch {
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7428
|
+
return null;
|
|
7429
|
+
},
|
|
7430
|
+
config: {
|
|
7431
|
+
installCmd: "uv sync",
|
|
7432
|
+
cacheEnvVar: "UV_CACHE_DIR"
|
|
7433
|
+
}
|
|
7434
|
+
},
|
|
7435
|
+
{
|
|
7436
|
+
name: "pip",
|
|
7437
|
+
detector: (p) => fs14.existsSync(path15.join(p, "requirements.txt")) ? "requirements.txt" : null,
|
|
7438
|
+
config: {
|
|
7439
|
+
installCmd: "pip install -r requirements.txt"
|
|
7440
|
+
}
|
|
7441
|
+
}
|
|
7442
|
+
]
|
|
7443
|
+
// Future: Add more languages here
|
|
7444
|
+
// rust: [
|
|
7445
|
+
// {
|
|
7446
|
+
// name: 'cargo',
|
|
7447
|
+
// detector: (p) => fs.existsSync(path.join(p, 'Cargo.toml')) ? 'Cargo.toml' : null,
|
|
7448
|
+
// config: { installCmd: 'cargo build' }
|
|
7449
|
+
// }
|
|
7450
|
+
// ],
|
|
7451
|
+
// go: [
|
|
7452
|
+
// {
|
|
7453
|
+
// name: 'go',
|
|
7454
|
+
// detector: (p) => fs.existsSync(path.join(p, 'go.mod')) ? 'go.mod' : null,
|
|
7455
|
+
// config: { installCmd: 'go mod download' }
|
|
7456
|
+
// }
|
|
7457
|
+
// ],
|
|
7458
|
+
};
|
|
7459
|
+
function detectPackageManager(worktreePath, preferredLanguages) {
|
|
7460
|
+
const checkedFiles = [];
|
|
7461
|
+
const languages = preferredLanguages || Object.keys(PACKAGE_MANAGERS);
|
|
7462
|
+
for (const language of languages) {
|
|
7463
|
+
const managers = PACKAGE_MANAGERS[language];
|
|
7464
|
+
if (!managers) continue;
|
|
7465
|
+
for (const { name, detector, config } of managers) {
|
|
7466
|
+
const matchedFile = detector(worktreePath);
|
|
7467
|
+
if (matchedFile) {
|
|
7468
|
+
checkedFiles.push(matchedFile);
|
|
7469
|
+
return {
|
|
7470
|
+
detected: true,
|
|
7471
|
+
packageManager: {
|
|
7472
|
+
name,
|
|
7473
|
+
language,
|
|
7474
|
+
...config
|
|
7475
|
+
},
|
|
7476
|
+
checkedFiles,
|
|
7477
|
+
matchedFile
|
|
7478
|
+
};
|
|
7479
|
+
}
|
|
7480
|
+
}
|
|
7481
|
+
}
|
|
7482
|
+
return {
|
|
7483
|
+
detected: false,
|
|
7484
|
+
checkedFiles
|
|
7485
|
+
};
|
|
7486
|
+
}
|
|
7487
|
+
|
|
7335
7488
|
// src/daemon/handlers/worktree-handlers.ts
|
|
7336
7489
|
async function getConfigForApi() {
|
|
7337
7490
|
if (process.env.EPISODA_MODE === "cloud" && process.env.EPISODA_ACCESS_TOKEN) {
|
|
@@ -7350,6 +7503,16 @@ async function getConfigForApi() {
|
|
|
7350
7503
|
return (0, import_core9.loadConfig)();
|
|
7351
7504
|
}
|
|
7352
7505
|
var execAsync = (0, import_util.promisify)(import_child_process8.exec);
|
|
7506
|
+
async function autoDetectSetupScript(worktreePath) {
|
|
7507
|
+
const detection = detectPackageManager(worktreePath);
|
|
7508
|
+
if (!detection.detected || !detection.packageManager) {
|
|
7509
|
+
console.log(`[Worktree] EP1222: No package manager detected in ${worktreePath}`);
|
|
7510
|
+
return null;
|
|
7511
|
+
}
|
|
7512
|
+
const { name, language, installCmd } = detection.packageManager;
|
|
7513
|
+
console.log(`[Worktree] EP1222: Detected ${name} (${language}) via ${detection.matchedFile}`);
|
|
7514
|
+
return installCmd;
|
|
7515
|
+
}
|
|
7353
7516
|
async function handleWorktreeCreate(request2) {
|
|
7354
7517
|
const {
|
|
7355
7518
|
workspaceSlug,
|
|
@@ -7361,11 +7524,13 @@ async function handleWorktreeCreate(request2) {
|
|
|
7361
7524
|
createBranch,
|
|
7362
7525
|
repoUrl,
|
|
7363
7526
|
envVars,
|
|
7364
|
-
setupScript
|
|
7527
|
+
setupScript,
|
|
7528
|
+
detachedHead = false
|
|
7529
|
+
// EP1223: Default to false for backward compatibility
|
|
7365
7530
|
} = request2;
|
|
7366
7531
|
console.log(`[Worktree] K1273: Creating worktree for ${moduleUid}`);
|
|
7367
7532
|
console.log(`[Worktree] Project: ${projectSlug}`);
|
|
7368
|
-
console.log(`[Worktree] Branch: ${branchName} (create: ${createBranch})`);
|
|
7533
|
+
console.log(`[Worktree] Branch: ${branchName} (create: ${createBranch}, detached: ${detachedHead})`);
|
|
7369
7534
|
if (!validateModuleUid(moduleUid)) {
|
|
7370
7535
|
return {
|
|
7371
7536
|
success: false,
|
|
@@ -7374,18 +7539,18 @@ async function handleWorktreeCreate(request2) {
|
|
|
7374
7539
|
}
|
|
7375
7540
|
try {
|
|
7376
7541
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
7377
|
-
const bareRepoPath =
|
|
7378
|
-
if (!
|
|
7542
|
+
const bareRepoPath = path16.join(projectPath, ".bare");
|
|
7543
|
+
if (!fs15.existsSync(bareRepoPath)) {
|
|
7379
7544
|
console.log(`[Worktree] K1273: Project not found, cloning lazily...`);
|
|
7380
7545
|
console.log(`[Worktree] Repo URL: ${repoUrl.replace(/\/\/[^@]*@/, "//***@")}`);
|
|
7381
|
-
const episodaDir =
|
|
7382
|
-
|
|
7546
|
+
const episodaDir = path16.join(projectPath, ".episoda");
|
|
7547
|
+
fs15.mkdirSync(episodaDir, { recursive: true });
|
|
7383
7548
|
try {
|
|
7384
7549
|
console.log(`[Worktree] K1273: Starting git clone...`);
|
|
7385
7550
|
await execAsync(`git clone --bare "${repoUrl}" "${bareRepoPath}"`);
|
|
7386
7551
|
console.log(`[Worktree] K1273: Clone successful`);
|
|
7387
7552
|
await execAsync(`git -C "${bareRepoPath}" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"`);
|
|
7388
|
-
const configPath =
|
|
7553
|
+
const configPath = path16.join(episodaDir, "config.json");
|
|
7389
7554
|
const config2 = {
|
|
7390
7555
|
projectId,
|
|
7391
7556
|
workspaceSlug,
|
|
@@ -7394,7 +7559,7 @@ async function handleWorktreeCreate(request2) {
|
|
|
7394
7559
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7395
7560
|
worktrees: []
|
|
7396
7561
|
};
|
|
7397
|
-
|
|
7562
|
+
fs15.writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf-8");
|
|
7398
7563
|
console.log(`[Worktree] K1273: Project initialized at ${projectPath}`);
|
|
7399
7564
|
} catch (cloneError) {
|
|
7400
7565
|
console.error(`[Worktree] K1273: Git clone failed: ${cloneError.message}`);
|
|
@@ -7419,7 +7584,7 @@ async function handleWorktreeCreate(request2) {
|
|
|
7419
7584
|
error: `Failed to initialize WorktreeManager at ${projectPath}`
|
|
7420
7585
|
};
|
|
7421
7586
|
}
|
|
7422
|
-
const result = await manager.createWorktree(moduleUid, branchName, createBranch);
|
|
7587
|
+
const result = await manager.createWorktree(moduleUid, branchName, createBranch, detachedHead);
|
|
7423
7588
|
if (!result.success) {
|
|
7424
7589
|
return {
|
|
7425
7590
|
success: false,
|
|
@@ -7442,11 +7607,12 @@ async function handleWorktreeCreate(request2) {
|
|
|
7442
7607
|
}
|
|
7443
7608
|
if (envVars && Object.keys(envVars).length > 0) {
|
|
7444
7609
|
const envContent = Object.entries(envVars).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
7445
|
-
const envPath =
|
|
7446
|
-
|
|
7610
|
+
const envPath = path16.join(worktreePath, ".env");
|
|
7611
|
+
fs15.writeFileSync(envPath, envContent + "\n", "utf-8");
|
|
7447
7612
|
console.log(`[Worktree] EP1143: Wrote ${Object.keys(envVars).length} env vars to .env`);
|
|
7448
7613
|
}
|
|
7449
|
-
|
|
7614
|
+
const effectiveSetupScript = setupScript || await autoDetectSetupScript(worktreePath);
|
|
7615
|
+
if (effectiveSetupScript) {
|
|
7450
7616
|
await manager.updateWorktreeStatus(moduleUid, "setup");
|
|
7451
7617
|
if (config?.access_token) {
|
|
7452
7618
|
await updateWorktree(config, {
|
|
@@ -7458,7 +7624,7 @@ async function handleWorktreeCreate(request2) {
|
|
|
7458
7624
|
});
|
|
7459
7625
|
}
|
|
7460
7626
|
console.log(`[Worktree] EP1143: Running setup script...`);
|
|
7461
|
-
const scriptResult = await manager.runSetupScript(moduleUid,
|
|
7627
|
+
const scriptResult = await manager.runSetupScript(moduleUid, effectiveSetupScript);
|
|
7462
7628
|
if (!scriptResult.success) {
|
|
7463
7629
|
console.error(`[Worktree] EP1143: Setup script failed: ${scriptResult.error}`);
|
|
7464
7630
|
await manager.updateWorktreeStatus(moduleUid, "error", scriptResult.error);
|
|
@@ -7616,12 +7782,12 @@ async function handleProjectEject(request2) {
|
|
|
7616
7782
|
console.log(`[Worktree] EP1144: Ejecting project ${projectSlug} from workspace ${workspaceSlug}`);
|
|
7617
7783
|
try {
|
|
7618
7784
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
7619
|
-
const bareRepoPath =
|
|
7620
|
-
if (!
|
|
7785
|
+
const bareRepoPath = path16.join(projectPath, ".bare");
|
|
7786
|
+
if (!fs15.existsSync(projectPath)) {
|
|
7621
7787
|
console.log(`[Worktree] EP1144: Project path not found, nothing to eject: ${projectPath}`);
|
|
7622
7788
|
return { success: true };
|
|
7623
7789
|
}
|
|
7624
|
-
if (!
|
|
7790
|
+
if (!fs15.existsSync(bareRepoPath)) {
|
|
7625
7791
|
console.log(`[Worktree] EP1144: Bare repo not found, nothing to eject: ${bareRepoPath}`);
|
|
7626
7792
|
return { success: true };
|
|
7627
7793
|
}
|
|
@@ -7636,12 +7802,12 @@ async function handleProjectEject(request2) {
|
|
|
7636
7802
|
};
|
|
7637
7803
|
}
|
|
7638
7804
|
}
|
|
7639
|
-
const artifactsPath =
|
|
7640
|
-
if (
|
|
7805
|
+
const artifactsPath = path16.join(projectPath, "artifacts");
|
|
7806
|
+
if (fs15.existsSync(artifactsPath)) {
|
|
7641
7807
|
await persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug);
|
|
7642
7808
|
}
|
|
7643
7809
|
console.log(`[Worktree] EP1144: Removing project directory: ${projectPath}`);
|
|
7644
|
-
await
|
|
7810
|
+
await fs15.promises.rm(projectPath, { recursive: true, force: true });
|
|
7645
7811
|
console.log(`[Worktree] EP1144: Successfully ejected project ${projectSlug}`);
|
|
7646
7812
|
return { success: true };
|
|
7647
7813
|
} catch (error) {
|
|
@@ -7655,7 +7821,7 @@ async function handleProjectEject(request2) {
|
|
|
7655
7821
|
async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug) {
|
|
7656
7822
|
const MAX_ARTIFACT_SIZE = 10 * 1024 * 1024;
|
|
7657
7823
|
try {
|
|
7658
|
-
const files = await
|
|
7824
|
+
const files = await fs15.promises.readdir(artifactsPath);
|
|
7659
7825
|
if (files.length === 0) {
|
|
7660
7826
|
return;
|
|
7661
7827
|
}
|
|
@@ -7669,8 +7835,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
7669
7835
|
}
|
|
7670
7836
|
const artifacts = [];
|
|
7671
7837
|
for (const fileName of files) {
|
|
7672
|
-
const filePath =
|
|
7673
|
-
const stat = await
|
|
7838
|
+
const filePath = path16.join(artifactsPath, fileName);
|
|
7839
|
+
const stat = await fs15.promises.stat(filePath);
|
|
7674
7840
|
if (stat.isDirectory()) {
|
|
7675
7841
|
continue;
|
|
7676
7842
|
}
|
|
@@ -7679,9 +7845,9 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
7679
7845
|
continue;
|
|
7680
7846
|
}
|
|
7681
7847
|
try {
|
|
7682
|
-
const content = await
|
|
7848
|
+
const content = await fs15.promises.readFile(filePath);
|
|
7683
7849
|
const base64Content = content.toString("base64");
|
|
7684
|
-
const ext =
|
|
7850
|
+
const ext = path16.extname(fileName).toLowerCase();
|
|
7685
7851
|
const mimeTypes = {
|
|
7686
7852
|
".json": "application/json",
|
|
7687
7853
|
".txt": "text/plain",
|
|
@@ -7737,8 +7903,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
|
|
|
7737
7903
|
}
|
|
7738
7904
|
|
|
7739
7905
|
// src/daemon/handlers/project-handlers.ts
|
|
7740
|
-
var
|
|
7741
|
-
var
|
|
7906
|
+
var path17 = __toESM(require("path"));
|
|
7907
|
+
var fs16 = __toESM(require("fs"));
|
|
7742
7908
|
function validateSlug(slug, fieldName) {
|
|
7743
7909
|
if (!slug || typeof slug !== "string") {
|
|
7744
7910
|
return `${fieldName} is required`;
|
|
@@ -7778,14 +7944,14 @@ async function handleProjectSetup(params) {
|
|
|
7778
7944
|
console.log(`[ProjectSetup] EP1199: Setting up project ${workspaceSlug}/${projectSlug}`);
|
|
7779
7945
|
try {
|
|
7780
7946
|
const projectPath = getProjectPath(workspaceSlug, projectSlug);
|
|
7781
|
-
const artifactsPath =
|
|
7782
|
-
const configDir =
|
|
7783
|
-
const configPath =
|
|
7784
|
-
await
|
|
7785
|
-
await
|
|
7947
|
+
const artifactsPath = path17.join(projectPath, "artifacts");
|
|
7948
|
+
const configDir = path17.join(projectPath, ".episoda");
|
|
7949
|
+
const configPath = path17.join(configDir, "config.json");
|
|
7950
|
+
await fs16.promises.mkdir(artifactsPath, { recursive: true });
|
|
7951
|
+
await fs16.promises.mkdir(configDir, { recursive: true });
|
|
7786
7952
|
let existingConfig = {};
|
|
7787
7953
|
try {
|
|
7788
|
-
const existing = await
|
|
7954
|
+
const existing = await fs16.promises.readFile(configPath, "utf-8");
|
|
7789
7955
|
existingConfig = JSON.parse(existing);
|
|
7790
7956
|
} catch {
|
|
7791
7957
|
}
|
|
@@ -7798,7 +7964,7 @@ async function handleProjectSetup(params) {
|
|
|
7798
7964
|
// Only set created_at if not already present
|
|
7799
7965
|
created_at: existingConfig.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
7800
7966
|
};
|
|
7801
|
-
await
|
|
7967
|
+
await fs16.promises.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
7802
7968
|
console.log(`[ProjectSetup] EP1199: Project setup complete at ${projectPath}`);
|
|
7803
7969
|
return {
|
|
7804
7970
|
success: true,
|
|
@@ -7912,12 +8078,12 @@ async function cleanupStaleCommits(projectPath) {
|
|
|
7912
8078
|
|
|
7913
8079
|
// src/agent/claude-binary.ts
|
|
7914
8080
|
var import_child_process10 = require("child_process");
|
|
7915
|
-
var
|
|
7916
|
-
var
|
|
8081
|
+
var path18 = __toESM(require("path"));
|
|
8082
|
+
var fs17 = __toESM(require("fs"));
|
|
7917
8083
|
var cachedBinaryPath = null;
|
|
7918
8084
|
function isValidClaudeBinary(binaryPath) {
|
|
7919
8085
|
try {
|
|
7920
|
-
|
|
8086
|
+
fs17.accessSync(binaryPath, fs17.constants.X_OK);
|
|
7921
8087
|
const version = (0, import_child_process10.execSync)(`"${binaryPath}" --version`, {
|
|
7922
8088
|
encoding: "utf-8",
|
|
7923
8089
|
timeout: 5e3,
|
|
@@ -7950,14 +8116,14 @@ async function ensureClaudeBinary() {
|
|
|
7950
8116
|
}
|
|
7951
8117
|
const bundledPaths = [
|
|
7952
8118
|
// In production: node_modules/.bin/claude
|
|
7953
|
-
|
|
8119
|
+
path18.join(__dirname, "..", "..", "node_modules", ".bin", "claude"),
|
|
7954
8120
|
// In monorepo development: packages/episoda/node_modules/.bin/claude
|
|
7955
|
-
|
|
8121
|
+
path18.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "claude"),
|
|
7956
8122
|
// Root monorepo node_modules
|
|
7957
|
-
|
|
8123
|
+
path18.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "claude")
|
|
7958
8124
|
];
|
|
7959
8125
|
for (const bundledPath of bundledPaths) {
|
|
7960
|
-
if (
|
|
8126
|
+
if (fs17.existsSync(bundledPath) && isValidClaudeBinary(bundledPath)) {
|
|
7961
8127
|
cachedBinaryPath = bundledPath;
|
|
7962
8128
|
return cachedBinaryPath;
|
|
7963
8129
|
}
|
|
@@ -7983,12 +8149,12 @@ async function ensureClaudeBinary() {
|
|
|
7983
8149
|
|
|
7984
8150
|
// src/agent/codex-binary.ts
|
|
7985
8151
|
var import_child_process11 = require("child_process");
|
|
7986
|
-
var
|
|
7987
|
-
var
|
|
8152
|
+
var path19 = __toESM(require("path"));
|
|
8153
|
+
var fs18 = __toESM(require("fs"));
|
|
7988
8154
|
var cachedBinaryPath2 = null;
|
|
7989
8155
|
function isValidCodexBinary(binaryPath) {
|
|
7990
8156
|
try {
|
|
7991
|
-
|
|
8157
|
+
fs18.accessSync(binaryPath, fs18.constants.X_OK);
|
|
7992
8158
|
const version = (0, import_child_process11.execSync)(`"${binaryPath}" --version`, {
|
|
7993
8159
|
encoding: "utf-8",
|
|
7994
8160
|
timeout: 5e3,
|
|
@@ -8021,14 +8187,14 @@ async function ensureCodexBinary() {
|
|
|
8021
8187
|
}
|
|
8022
8188
|
const bundledPaths = [
|
|
8023
8189
|
// In production: node_modules/.bin/codex
|
|
8024
|
-
|
|
8190
|
+
path19.join(__dirname, "..", "..", "node_modules", ".bin", "codex"),
|
|
8025
8191
|
// In monorepo development: packages/episoda/node_modules/.bin/codex
|
|
8026
|
-
|
|
8192
|
+
path19.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "codex"),
|
|
8027
8193
|
// Root monorepo node_modules
|
|
8028
|
-
|
|
8194
|
+
path19.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "codex")
|
|
8029
8195
|
];
|
|
8030
8196
|
for (const bundledPath of bundledPaths) {
|
|
8031
|
-
if (
|
|
8197
|
+
if (fs18.existsSync(bundledPath) && isValidCodexBinary(bundledPath)) {
|
|
8032
8198
|
cachedBinaryPath2 = bundledPath;
|
|
8033
8199
|
return cachedBinaryPath2;
|
|
8034
8200
|
}
|
|
@@ -8095,8 +8261,8 @@ function generateCodexConfig(credentials, projectPath) {
|
|
|
8095
8261
|
|
|
8096
8262
|
// src/agent/agent-manager.ts
|
|
8097
8263
|
var import_child_process12 = require("child_process");
|
|
8098
|
-
var
|
|
8099
|
-
var
|
|
8264
|
+
var path20 = __toESM(require("path"));
|
|
8265
|
+
var fs19 = __toESM(require("fs"));
|
|
8100
8266
|
var os6 = __toESM(require("os"));
|
|
8101
8267
|
|
|
8102
8268
|
// src/agent/claude-config.ts
|
|
@@ -8419,7 +8585,7 @@ var AgentManager = class {
|
|
|
8419
8585
|
this.initialized = false;
|
|
8420
8586
|
// EP1133: Lock for config file writes to prevent race conditions
|
|
8421
8587
|
this.configWriteLock = Promise.resolve();
|
|
8422
|
-
this.pidDir =
|
|
8588
|
+
this.pidDir = path20.join(os6.homedir(), ".episoda", "agent-pids");
|
|
8423
8589
|
}
|
|
8424
8590
|
/**
|
|
8425
8591
|
* EP1133: Acquire lock for config file writes
|
|
@@ -8448,8 +8614,8 @@ var AgentManager = class {
|
|
|
8448
8614
|
return;
|
|
8449
8615
|
}
|
|
8450
8616
|
console.log("[AgentManager] Initializing...");
|
|
8451
|
-
if (!
|
|
8452
|
-
|
|
8617
|
+
if (!fs19.existsSync(this.pidDir)) {
|
|
8618
|
+
fs19.mkdirSync(this.pidDir, { recursive: true });
|
|
8453
8619
|
}
|
|
8454
8620
|
await this.cleanupOrphanedProcesses();
|
|
8455
8621
|
try {
|
|
@@ -8696,9 +8862,9 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8696
8862
|
const useApiKey = !useOAuth && !!session.credentials.apiKey;
|
|
8697
8863
|
if (provider === "codex") {
|
|
8698
8864
|
await this.withConfigLock(async () => {
|
|
8699
|
-
const codexDir =
|
|
8700
|
-
if (!
|
|
8701
|
-
|
|
8865
|
+
const codexDir = path20.join(os6.homedir(), ".codex");
|
|
8866
|
+
if (!fs19.existsSync(codexDir)) {
|
|
8867
|
+
fs19.mkdirSync(codexDir, { recursive: true });
|
|
8702
8868
|
}
|
|
8703
8869
|
if (useOAuth) {
|
|
8704
8870
|
const codexConfig = generateCodexConfig({
|
|
@@ -8708,21 +8874,21 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8708
8874
|
accountId: session.credentials.accountId,
|
|
8709
8875
|
expiresAt: session.credentials.expiresAt
|
|
8710
8876
|
}, session.projectPath);
|
|
8711
|
-
const authJsonPath =
|
|
8712
|
-
|
|
8877
|
+
const authJsonPath = path20.join(codexDir, "auth.json");
|
|
8878
|
+
fs19.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
|
|
8713
8879
|
console.log("[AgentManager] EP1133: Wrote Codex auth.json to ~/.codex/auth.json");
|
|
8714
8880
|
if (codexConfig["config.toml"]) {
|
|
8715
|
-
const configTomlPath =
|
|
8881
|
+
const configTomlPath = path20.join(codexDir, "config.toml");
|
|
8716
8882
|
let existingConfig = "";
|
|
8717
8883
|
try {
|
|
8718
|
-
existingConfig =
|
|
8884
|
+
existingConfig = fs19.readFileSync(configTomlPath, "utf-8");
|
|
8719
8885
|
} catch {
|
|
8720
8886
|
}
|
|
8721
8887
|
const escapedPathForRegex = session.projectPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8722
8888
|
const projectKeyPattern = new RegExp(`\\[projects\\."${escapedPathForRegex}"\\]`);
|
|
8723
8889
|
const projectAlreadyTrusted = projectKeyPattern.test(existingConfig);
|
|
8724
8890
|
if (!projectAlreadyTrusted) {
|
|
8725
|
-
|
|
8891
|
+
fs19.writeFileSync(configTomlPath, existingConfig + "\n" + codexConfig["config.toml"], { mode: 420 });
|
|
8726
8892
|
console.log("[AgentManager] EP1133: Updated Codex config.toml with project trust");
|
|
8727
8893
|
}
|
|
8728
8894
|
}
|
|
@@ -8732,14 +8898,14 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8732
8898
|
});
|
|
8733
8899
|
} else {
|
|
8734
8900
|
await this.withConfigLock(async () => {
|
|
8735
|
-
const claudeDir =
|
|
8736
|
-
const credentialsPath =
|
|
8737
|
-
const statsigDir =
|
|
8738
|
-
if (!
|
|
8739
|
-
|
|
8901
|
+
const claudeDir = path20.join(os6.homedir(), ".claude");
|
|
8902
|
+
const credentialsPath = path20.join(claudeDir, ".credentials.json");
|
|
8903
|
+
const statsigDir = path20.join(claudeDir, "statsig");
|
|
8904
|
+
if (!fs19.existsSync(claudeDir)) {
|
|
8905
|
+
fs19.mkdirSync(claudeDir, { recursive: true });
|
|
8740
8906
|
}
|
|
8741
|
-
if (!
|
|
8742
|
-
|
|
8907
|
+
if (!fs19.existsSync(statsigDir)) {
|
|
8908
|
+
fs19.mkdirSync(statsigDir, { recursive: true });
|
|
8743
8909
|
}
|
|
8744
8910
|
if (useOAuth) {
|
|
8745
8911
|
const oauthCredentials = {
|
|
@@ -8757,7 +8923,7 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8757
8923
|
const credentialsContent = JSON.stringify({
|
|
8758
8924
|
claudeAiOauth: oauthCredentials
|
|
8759
8925
|
}, null, 2);
|
|
8760
|
-
|
|
8926
|
+
fs19.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
|
|
8761
8927
|
console.log("[AgentManager] Wrote OAuth credentials to ~/.claude/.credentials.json");
|
|
8762
8928
|
try {
|
|
8763
8929
|
const claudeConfig = generateClaudeConfig({
|
|
@@ -8769,11 +8935,11 @@ If changes are needed, explain what needs to be done.`;
|
|
|
8769
8935
|
if (!hasEvaluations || !hasStableId) {
|
|
8770
8936
|
throw new Error(`Invalid statsig config: missing required files`);
|
|
8771
8937
|
}
|
|
8772
|
-
const settingsPath =
|
|
8773
|
-
|
|
8938
|
+
const settingsPath = path20.join(claudeDir, "settings.json");
|
|
8939
|
+
fs19.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
|
|
8774
8940
|
for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
|
|
8775
|
-
const filePath =
|
|
8776
|
-
|
|
8941
|
+
const filePath = path20.join(statsigDir, filename);
|
|
8942
|
+
fs19.writeFileSync(filePath, content, { mode: 420 });
|
|
8777
8943
|
}
|
|
8778
8944
|
if (session.credentials.githubToken) {
|
|
8779
8945
|
console.log("[AgentManager] EP1146: GitHub MCP server enabled with installation token");
|
|
@@ -9043,14 +9209,14 @@ If changes are needed, explain what needs to be done.`;
|
|
|
9043
9209
|
*/
|
|
9044
9210
|
async cleanupOrphanedProcesses() {
|
|
9045
9211
|
let cleaned = 0;
|
|
9046
|
-
if (!
|
|
9212
|
+
if (!fs19.existsSync(this.pidDir)) {
|
|
9047
9213
|
return { cleaned };
|
|
9048
9214
|
}
|
|
9049
|
-
const pidFiles =
|
|
9215
|
+
const pidFiles = fs19.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
|
|
9050
9216
|
for (const pidFile of pidFiles) {
|
|
9051
|
-
const pidPath =
|
|
9217
|
+
const pidPath = path20.join(this.pidDir, pidFile);
|
|
9052
9218
|
try {
|
|
9053
|
-
const pidStr =
|
|
9219
|
+
const pidStr = fs19.readFileSync(pidPath, "utf-8").trim();
|
|
9054
9220
|
const pid = parseInt(pidStr, 10);
|
|
9055
9221
|
if (!isNaN(pid)) {
|
|
9056
9222
|
try {
|
|
@@ -9061,7 +9227,7 @@ If changes are needed, explain what needs to be done.`;
|
|
|
9061
9227
|
} catch {
|
|
9062
9228
|
}
|
|
9063
9229
|
}
|
|
9064
|
-
|
|
9230
|
+
fs19.unlinkSync(pidPath);
|
|
9065
9231
|
} catch (error) {
|
|
9066
9232
|
console.warn(`[AgentManager] Error cleaning PID file ${pidFile}:`, error);
|
|
9067
9233
|
}
|
|
@@ -9075,17 +9241,17 @@ If changes are needed, explain what needs to be done.`;
|
|
|
9075
9241
|
* Write PID file for session tracking
|
|
9076
9242
|
*/
|
|
9077
9243
|
writePidFile(sessionId, pid) {
|
|
9078
|
-
const pidPath =
|
|
9079
|
-
|
|
9244
|
+
const pidPath = path20.join(this.pidDir, `${sessionId}.pid`);
|
|
9245
|
+
fs19.writeFileSync(pidPath, pid.toString());
|
|
9080
9246
|
}
|
|
9081
9247
|
/**
|
|
9082
9248
|
* Remove PID file for session
|
|
9083
9249
|
*/
|
|
9084
9250
|
removePidFile(sessionId) {
|
|
9085
|
-
const pidPath =
|
|
9251
|
+
const pidPath = path20.join(this.pidDir, `${sessionId}.pid`);
|
|
9086
9252
|
try {
|
|
9087
|
-
if (
|
|
9088
|
-
|
|
9253
|
+
if (fs19.existsSync(pidPath)) {
|
|
9254
|
+
fs19.unlinkSync(pidPath);
|
|
9089
9255
|
}
|
|
9090
9256
|
} catch {
|
|
9091
9257
|
}
|
|
@@ -9095,8 +9261,8 @@ If changes are needed, explain what needs to be done.`;
|
|
|
9095
9261
|
// src/utils/dev-server.ts
|
|
9096
9262
|
var import_child_process13 = require("child_process");
|
|
9097
9263
|
var import_core11 = __toESM(require_dist());
|
|
9098
|
-
var
|
|
9099
|
-
var
|
|
9264
|
+
var fs20 = __toESM(require("fs"));
|
|
9265
|
+
var path21 = __toESM(require("path"));
|
|
9100
9266
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
9101
9267
|
var INITIAL_RESTART_DELAY_MS = 2e3;
|
|
9102
9268
|
var MAX_RESTART_DELAY_MS = 3e4;
|
|
@@ -9104,26 +9270,26 @@ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
|
|
|
9104
9270
|
var NODE_MEMORY_LIMIT_MB = 2048;
|
|
9105
9271
|
var activeServers = /* @__PURE__ */ new Map();
|
|
9106
9272
|
function getLogsDir() {
|
|
9107
|
-
const logsDir =
|
|
9108
|
-
if (!
|
|
9109
|
-
|
|
9273
|
+
const logsDir = path21.join((0, import_core11.getConfigDir)(), "logs");
|
|
9274
|
+
if (!fs20.existsSync(logsDir)) {
|
|
9275
|
+
fs20.mkdirSync(logsDir, { recursive: true });
|
|
9110
9276
|
}
|
|
9111
9277
|
return logsDir;
|
|
9112
9278
|
}
|
|
9113
9279
|
function getLogFilePath(moduleUid) {
|
|
9114
|
-
return
|
|
9280
|
+
return path21.join(getLogsDir(), `dev-${moduleUid}.log`);
|
|
9115
9281
|
}
|
|
9116
9282
|
function rotateLogIfNeeded(logPath) {
|
|
9117
9283
|
try {
|
|
9118
|
-
if (
|
|
9119
|
-
const stats =
|
|
9284
|
+
if (fs20.existsSync(logPath)) {
|
|
9285
|
+
const stats = fs20.statSync(logPath);
|
|
9120
9286
|
if (stats.size > MAX_LOG_SIZE_BYTES) {
|
|
9121
9287
|
const backupPath = `${logPath}.1`;
|
|
9122
|
-
if (
|
|
9123
|
-
|
|
9288
|
+
if (fs20.existsSync(backupPath)) {
|
|
9289
|
+
fs20.unlinkSync(backupPath);
|
|
9124
9290
|
}
|
|
9125
|
-
|
|
9126
|
-
console.log(`[DevServer] EP932: Rotated log file for ${
|
|
9291
|
+
fs20.renameSync(logPath, backupPath);
|
|
9292
|
+
console.log(`[DevServer] EP932: Rotated log file for ${path21.basename(logPath)}`);
|
|
9127
9293
|
}
|
|
9128
9294
|
}
|
|
9129
9295
|
} catch (error) {
|
|
@@ -9136,7 +9302,7 @@ function writeToLog(logPath, line, isError = false) {
|
|
|
9136
9302
|
const prefix = isError ? "ERR" : "OUT";
|
|
9137
9303
|
const logLine = `[${timestamp}] [${prefix}] ${line}
|
|
9138
9304
|
`;
|
|
9139
|
-
|
|
9305
|
+
fs20.appendFileSync(logPath, logLine);
|
|
9140
9306
|
} catch {
|
|
9141
9307
|
}
|
|
9142
9308
|
}
|
|
@@ -9315,8 +9481,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
|
|
|
9315
9481
|
});
|
|
9316
9482
|
injectedEnvVars = result.envVars;
|
|
9317
9483
|
console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
|
|
9318
|
-
const envFilePath =
|
|
9319
|
-
if (!
|
|
9484
|
+
const envFilePath = path21.join(projectPath, ".env");
|
|
9485
|
+
if (!fs20.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
|
|
9320
9486
|
console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
|
|
9321
9487
|
writeEnvFile(projectPath, injectedEnvVars);
|
|
9322
9488
|
}
|
|
@@ -9422,19 +9588,19 @@ function getDevServerStatus() {
|
|
|
9422
9588
|
}
|
|
9423
9589
|
|
|
9424
9590
|
// src/utils/worktree.ts
|
|
9425
|
-
var
|
|
9426
|
-
var
|
|
9591
|
+
var path22 = __toESM(require("path"));
|
|
9592
|
+
var fs21 = __toESM(require("fs"));
|
|
9427
9593
|
var os7 = __toESM(require("os"));
|
|
9428
9594
|
var import_core12 = __toESM(require_dist());
|
|
9429
9595
|
function getEpisodaRoot2() {
|
|
9430
|
-
return process.env.EPISODA_ROOT ||
|
|
9596
|
+
return process.env.EPISODA_ROOT || path22.join(os7.homedir(), "episoda");
|
|
9431
9597
|
}
|
|
9432
9598
|
function getWorktreeInfo(moduleUid, workspaceSlug, projectSlug) {
|
|
9433
9599
|
const root = getEpisodaRoot2();
|
|
9434
|
-
const worktreePath =
|
|
9600
|
+
const worktreePath = path22.join(root, workspaceSlug, projectSlug, moduleUid);
|
|
9435
9601
|
return {
|
|
9436
9602
|
path: worktreePath,
|
|
9437
|
-
exists:
|
|
9603
|
+
exists: fs21.existsSync(worktreePath),
|
|
9438
9604
|
moduleUid
|
|
9439
9605
|
};
|
|
9440
9606
|
}
|
|
@@ -9448,15 +9614,15 @@ async function getWorktreeInfoForModule(moduleUid) {
|
|
|
9448
9614
|
return null;
|
|
9449
9615
|
}
|
|
9450
9616
|
const root = getEpisodaRoot2();
|
|
9451
|
-
const workspaceRoot =
|
|
9617
|
+
const workspaceRoot = path22.join(root, config.workspace_slug);
|
|
9452
9618
|
try {
|
|
9453
|
-
const entries =
|
|
9619
|
+
const entries = fs21.readdirSync(workspaceRoot, { withFileTypes: true });
|
|
9454
9620
|
for (const entry of entries) {
|
|
9455
9621
|
if (!entry.isDirectory()) {
|
|
9456
9622
|
continue;
|
|
9457
9623
|
}
|
|
9458
|
-
const worktreePath =
|
|
9459
|
-
if (
|
|
9624
|
+
const worktreePath = path22.join(workspaceRoot, entry.name, moduleUid);
|
|
9625
|
+
if (fs21.existsSync(worktreePath)) {
|
|
9460
9626
|
return {
|
|
9461
9627
|
path: worktreePath,
|
|
9462
9628
|
exists: true,
|
|
@@ -9473,61 +9639,61 @@ async function getWorktreeInfoForModule(moduleUid) {
|
|
|
9473
9639
|
}
|
|
9474
9640
|
|
|
9475
9641
|
// src/framework-detector.ts
|
|
9476
|
-
var
|
|
9477
|
-
var
|
|
9478
|
-
function
|
|
9479
|
-
if (
|
|
9642
|
+
var fs22 = __toESM(require("fs"));
|
|
9643
|
+
var path23 = __toESM(require("path"));
|
|
9644
|
+
function getInstallCommand2(cwd) {
|
|
9645
|
+
if (fs22.existsSync(path23.join(cwd, "bun.lockb"))) {
|
|
9480
9646
|
return {
|
|
9481
9647
|
command: ["bun", "install"],
|
|
9482
9648
|
description: "Installing dependencies with bun",
|
|
9483
9649
|
detectedFrom: "bun.lockb"
|
|
9484
9650
|
};
|
|
9485
9651
|
}
|
|
9486
|
-
if (
|
|
9652
|
+
if (fs22.existsSync(path23.join(cwd, "pnpm-lock.yaml"))) {
|
|
9487
9653
|
return {
|
|
9488
9654
|
command: ["pnpm", "install"],
|
|
9489
9655
|
description: "Installing dependencies with pnpm",
|
|
9490
9656
|
detectedFrom: "pnpm-lock.yaml"
|
|
9491
9657
|
};
|
|
9492
9658
|
}
|
|
9493
|
-
if (
|
|
9659
|
+
if (fs22.existsSync(path23.join(cwd, "yarn.lock"))) {
|
|
9494
9660
|
return {
|
|
9495
9661
|
command: ["yarn", "install"],
|
|
9496
9662
|
description: "Installing dependencies with yarn",
|
|
9497
9663
|
detectedFrom: "yarn.lock"
|
|
9498
9664
|
};
|
|
9499
9665
|
}
|
|
9500
|
-
if (
|
|
9666
|
+
if (fs22.existsSync(path23.join(cwd, "package-lock.json"))) {
|
|
9501
9667
|
return {
|
|
9502
9668
|
command: ["npm", "ci"],
|
|
9503
9669
|
description: "Installing dependencies with npm ci",
|
|
9504
9670
|
detectedFrom: "package-lock.json"
|
|
9505
9671
|
};
|
|
9506
9672
|
}
|
|
9507
|
-
if (
|
|
9673
|
+
if (fs22.existsSync(path23.join(cwd, "package.json"))) {
|
|
9508
9674
|
return {
|
|
9509
9675
|
command: ["npm", "install"],
|
|
9510
9676
|
description: "Installing dependencies with npm",
|
|
9511
9677
|
detectedFrom: "package.json"
|
|
9512
9678
|
};
|
|
9513
9679
|
}
|
|
9514
|
-
if (
|
|
9680
|
+
if (fs22.existsSync(path23.join(cwd, "Pipfile.lock")) || fs22.existsSync(path23.join(cwd, "Pipfile"))) {
|
|
9515
9681
|
return {
|
|
9516
9682
|
command: ["pipenv", "install"],
|
|
9517
9683
|
description: "Installing dependencies with pipenv",
|
|
9518
|
-
detectedFrom:
|
|
9684
|
+
detectedFrom: fs22.existsSync(path23.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
|
|
9519
9685
|
};
|
|
9520
9686
|
}
|
|
9521
|
-
if (
|
|
9687
|
+
if (fs22.existsSync(path23.join(cwd, "poetry.lock"))) {
|
|
9522
9688
|
return {
|
|
9523
9689
|
command: ["poetry", "install"],
|
|
9524
9690
|
description: "Installing dependencies with poetry",
|
|
9525
9691
|
detectedFrom: "poetry.lock"
|
|
9526
9692
|
};
|
|
9527
9693
|
}
|
|
9528
|
-
if (
|
|
9529
|
-
const pyprojectPath =
|
|
9530
|
-
const content =
|
|
9694
|
+
if (fs22.existsSync(path23.join(cwd, "pyproject.toml"))) {
|
|
9695
|
+
const pyprojectPath = path23.join(cwd, "pyproject.toml");
|
|
9696
|
+
const content = fs22.readFileSync(pyprojectPath, "utf-8");
|
|
9531
9697
|
if (content.includes("[tool.poetry]")) {
|
|
9532
9698
|
return {
|
|
9533
9699
|
command: ["poetry", "install"],
|
|
@@ -9536,42 +9702,42 @@ function getInstallCommand(cwd) {
|
|
|
9536
9702
|
};
|
|
9537
9703
|
}
|
|
9538
9704
|
}
|
|
9539
|
-
if (
|
|
9705
|
+
if (fs22.existsSync(path23.join(cwd, "requirements.txt"))) {
|
|
9540
9706
|
return {
|
|
9541
9707
|
command: ["pip", "install", "-r", "requirements.txt"],
|
|
9542
9708
|
description: "Installing dependencies with pip",
|
|
9543
9709
|
detectedFrom: "requirements.txt"
|
|
9544
9710
|
};
|
|
9545
9711
|
}
|
|
9546
|
-
if (
|
|
9712
|
+
if (fs22.existsSync(path23.join(cwd, "Gemfile.lock")) || fs22.existsSync(path23.join(cwd, "Gemfile"))) {
|
|
9547
9713
|
return {
|
|
9548
9714
|
command: ["bundle", "install"],
|
|
9549
9715
|
description: "Installing dependencies with bundler",
|
|
9550
|
-
detectedFrom:
|
|
9716
|
+
detectedFrom: fs22.existsSync(path23.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
|
|
9551
9717
|
};
|
|
9552
9718
|
}
|
|
9553
|
-
if (
|
|
9719
|
+
if (fs22.existsSync(path23.join(cwd, "go.sum")) || fs22.existsSync(path23.join(cwd, "go.mod"))) {
|
|
9554
9720
|
return {
|
|
9555
9721
|
command: ["go", "mod", "download"],
|
|
9556
9722
|
description: "Downloading Go modules",
|
|
9557
|
-
detectedFrom:
|
|
9723
|
+
detectedFrom: fs22.existsSync(path23.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
|
|
9558
9724
|
};
|
|
9559
9725
|
}
|
|
9560
|
-
if (
|
|
9726
|
+
if (fs22.existsSync(path23.join(cwd, "Cargo.lock")) || fs22.existsSync(path23.join(cwd, "Cargo.toml"))) {
|
|
9561
9727
|
return {
|
|
9562
9728
|
command: ["cargo", "build"],
|
|
9563
9729
|
description: "Building Rust project (downloads dependencies)",
|
|
9564
|
-
detectedFrom:
|
|
9730
|
+
detectedFrom: fs22.existsSync(path23.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
|
|
9565
9731
|
};
|
|
9566
9732
|
}
|
|
9567
9733
|
return null;
|
|
9568
9734
|
}
|
|
9569
9735
|
|
|
9570
9736
|
// src/daemon/daemon-process.ts
|
|
9571
|
-
var
|
|
9737
|
+
var fs23 = __toESM(require("fs"));
|
|
9572
9738
|
var http2 = __toESM(require("http"));
|
|
9573
9739
|
var os8 = __toESM(require("os"));
|
|
9574
|
-
var
|
|
9740
|
+
var path24 = __toESM(require("path"));
|
|
9575
9741
|
var packageJson = require_package();
|
|
9576
9742
|
async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
|
|
9577
9743
|
const now = Date.now();
|
|
@@ -9792,9 +9958,9 @@ var Daemon = class _Daemon {
|
|
|
9792
9958
|
this.healthServer = http2.createServer((req, res) => {
|
|
9793
9959
|
if (req.url === "/health" || req.url === "/") {
|
|
9794
9960
|
const isConnected = this.liveConnections.size > 0;
|
|
9795
|
-
const projects = Array.from(this.connections.entries()).map(([
|
|
9796
|
-
path:
|
|
9797
|
-
connected: this.liveConnections.has(
|
|
9961
|
+
const projects = Array.from(this.connections.entries()).map(([path25, conn]) => ({
|
|
9962
|
+
path: path25,
|
|
9963
|
+
connected: this.liveConnections.has(path25)
|
|
9798
9964
|
}));
|
|
9799
9965
|
const status = {
|
|
9800
9966
|
status: isConnected ? "healthy" : "degraded",
|
|
@@ -10162,7 +10328,7 @@ var Daemon = class _Daemon {
|
|
|
10162
10328
|
client.updateActivity();
|
|
10163
10329
|
try {
|
|
10164
10330
|
const gitCmd = message.command;
|
|
10165
|
-
const bareRepoPath =
|
|
10331
|
+
const bareRepoPath = path24.join(projectPath, ".bare");
|
|
10166
10332
|
const cwd = gitCmd.worktreePath || bareRepoPath;
|
|
10167
10333
|
if (gitCmd.worktreePath) {
|
|
10168
10334
|
console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
|
|
@@ -10747,8 +10913,8 @@ var Daemon = class _Daemon {
|
|
|
10747
10913
|
let daemonPid;
|
|
10748
10914
|
try {
|
|
10749
10915
|
const pidPath = getPidFilePath();
|
|
10750
|
-
if (
|
|
10751
|
-
const pidStr =
|
|
10916
|
+
if (fs23.existsSync(pidPath)) {
|
|
10917
|
+
const pidStr = fs23.readFileSync(pidPath, "utf-8").trim();
|
|
10752
10918
|
daemonPid = parseInt(pidStr, 10);
|
|
10753
10919
|
}
|
|
10754
10920
|
} catch (pidError) {
|
|
@@ -10829,28 +10995,28 @@ var Daemon = class _Daemon {
|
|
|
10829
10995
|
* - workDir: The directory to run git commands in (cwd)
|
|
10830
10996
|
*/
|
|
10831
10997
|
getGitDirs(projectPath) {
|
|
10832
|
-
const bareDir =
|
|
10833
|
-
const gitPath =
|
|
10834
|
-
if (
|
|
10998
|
+
const bareDir = path24.join(projectPath, ".bare");
|
|
10999
|
+
const gitPath = path24.join(projectPath, ".git");
|
|
11000
|
+
if (fs23.existsSync(bareDir) && fs23.statSync(bareDir).isDirectory()) {
|
|
10835
11001
|
return { gitDir: bareDir, workDir: projectPath };
|
|
10836
11002
|
}
|
|
10837
|
-
if (
|
|
11003
|
+
if (fs23.existsSync(gitPath) && fs23.statSync(gitPath).isDirectory()) {
|
|
10838
11004
|
return { gitDir: null, workDir: projectPath };
|
|
10839
11005
|
}
|
|
10840
|
-
if (
|
|
11006
|
+
if (fs23.existsSync(gitPath) && fs23.statSync(gitPath).isFile()) {
|
|
10841
11007
|
return { gitDir: null, workDir: projectPath };
|
|
10842
11008
|
}
|
|
10843
|
-
const entries =
|
|
11009
|
+
const entries = fs23.readdirSync(projectPath, { withFileTypes: true });
|
|
10844
11010
|
for (const entry of entries) {
|
|
10845
11011
|
if (entry.isDirectory() && entry.name.startsWith("EP")) {
|
|
10846
|
-
const worktreePath =
|
|
10847
|
-
const worktreeGit =
|
|
10848
|
-
if (
|
|
11012
|
+
const worktreePath = path24.join(projectPath, entry.name);
|
|
11013
|
+
const worktreeGit = path24.join(worktreePath, ".git");
|
|
11014
|
+
if (fs23.existsSync(worktreeGit)) {
|
|
10849
11015
|
return { gitDir: null, workDir: worktreePath };
|
|
10850
11016
|
}
|
|
10851
11017
|
}
|
|
10852
11018
|
}
|
|
10853
|
-
if (
|
|
11019
|
+
if (fs23.existsSync(bareDir)) {
|
|
10854
11020
|
return { gitDir: bareDir, workDir: projectPath };
|
|
10855
11021
|
}
|
|
10856
11022
|
return { gitDir: null, workDir: projectPath };
|
|
@@ -10914,24 +11080,24 @@ var Daemon = class _Daemon {
|
|
|
10914
11080
|
async installGitHooks(projectPath) {
|
|
10915
11081
|
const hooks = ["post-checkout", "pre-commit", "post-commit"];
|
|
10916
11082
|
let hooksDir;
|
|
10917
|
-
const bareHooksDir =
|
|
10918
|
-
const gitHooksDir =
|
|
10919
|
-
if (
|
|
11083
|
+
const bareHooksDir = path24.join(projectPath, ".bare", "hooks");
|
|
11084
|
+
const gitHooksDir = path24.join(projectPath, ".git", "hooks");
|
|
11085
|
+
if (fs23.existsSync(bareHooksDir)) {
|
|
10920
11086
|
hooksDir = bareHooksDir;
|
|
10921
|
-
} else if (
|
|
11087
|
+
} else if (fs23.existsSync(gitHooksDir) && fs23.statSync(path24.join(projectPath, ".git")).isDirectory()) {
|
|
10922
11088
|
hooksDir = gitHooksDir;
|
|
10923
11089
|
} else {
|
|
10924
|
-
const parentBareHooks =
|
|
10925
|
-
if (
|
|
11090
|
+
const parentBareHooks = path24.join(projectPath, "..", ".bare", "hooks");
|
|
11091
|
+
if (fs23.existsSync(parentBareHooks)) {
|
|
10926
11092
|
hooksDir = parentBareHooks;
|
|
10927
11093
|
} else {
|
|
10928
11094
|
console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
|
|
10929
11095
|
return;
|
|
10930
11096
|
}
|
|
10931
11097
|
}
|
|
10932
|
-
if (!
|
|
11098
|
+
if (!fs23.existsSync(hooksDir)) {
|
|
10933
11099
|
try {
|
|
10934
|
-
|
|
11100
|
+
fs23.mkdirSync(hooksDir, { recursive: true });
|
|
10935
11101
|
} catch (error) {
|
|
10936
11102
|
console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
|
|
10937
11103
|
return;
|
|
@@ -10939,20 +11105,20 @@ var Daemon = class _Daemon {
|
|
|
10939
11105
|
}
|
|
10940
11106
|
for (const hookName of hooks) {
|
|
10941
11107
|
try {
|
|
10942
|
-
const hookPath =
|
|
10943
|
-
const bundledHookPath =
|
|
10944
|
-
if (!
|
|
11108
|
+
const hookPath = path24.join(hooksDir, hookName);
|
|
11109
|
+
const bundledHookPath = path24.join(__dirname, "..", "hooks", hookName);
|
|
11110
|
+
if (!fs23.existsSync(bundledHookPath)) {
|
|
10945
11111
|
console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
|
|
10946
11112
|
continue;
|
|
10947
11113
|
}
|
|
10948
|
-
const hookContent =
|
|
10949
|
-
if (
|
|
10950
|
-
const existingContent =
|
|
11114
|
+
const hookContent = fs23.readFileSync(bundledHookPath, "utf-8");
|
|
11115
|
+
if (fs23.existsSync(hookPath)) {
|
|
11116
|
+
const existingContent = fs23.readFileSync(hookPath, "utf-8");
|
|
10951
11117
|
if (existingContent === hookContent) {
|
|
10952
11118
|
continue;
|
|
10953
11119
|
}
|
|
10954
11120
|
}
|
|
10955
|
-
|
|
11121
|
+
fs23.writeFileSync(hookPath, hookContent, { mode: 493 });
|
|
10956
11122
|
console.log(`[Daemon] Installed git hook: ${hookName}`);
|
|
10957
11123
|
} catch (error) {
|
|
10958
11124
|
console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
|
|
@@ -11290,7 +11456,7 @@ var Daemon = class _Daemon {
|
|
|
11290
11456
|
console.log(`[Daemon] EP1002: Writing .env with ${Object.keys(envVars).length} variables`);
|
|
11291
11457
|
writeEnvFile(worktreePath, envVars);
|
|
11292
11458
|
}
|
|
11293
|
-
const installCmd =
|
|
11459
|
+
const installCmd = getInstallCommand2(worktreePath);
|
|
11294
11460
|
if (installCmd) {
|
|
11295
11461
|
console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
11296
11462
|
console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
|
|
@@ -11344,7 +11510,7 @@ var Daemon = class _Daemon {
|
|
|
11344
11510
|
console.warn(`[Daemon] EP964: File copy failed (non-fatal): ${copyResult.error}`);
|
|
11345
11511
|
}
|
|
11346
11512
|
}
|
|
11347
|
-
const installCmd =
|
|
11513
|
+
const installCmd = getInstallCommand2(worktreePath);
|
|
11348
11514
|
if (installCmd) {
|
|
11349
11515
|
console.log(`[Daemon] EP986: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
|
|
11350
11516
|
console.log(`[Daemon] EP986: Running: ${installCmd.command.join(" ")}`);
|
|
@@ -11956,8 +12122,8 @@ var Daemon = class _Daemon {
|
|
|
11956
12122
|
await this.shutdown();
|
|
11957
12123
|
try {
|
|
11958
12124
|
const pidPath = getPidFilePath();
|
|
11959
|
-
if (
|
|
11960
|
-
|
|
12125
|
+
if (fs23.existsSync(pidPath)) {
|
|
12126
|
+
fs23.unlinkSync(pidPath);
|
|
11961
12127
|
console.log("[Daemon] PID file cleaned up");
|
|
11962
12128
|
}
|
|
11963
12129
|
} catch (error) {
|