kantban-cli 0.1.51 → 0.1.54

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.
@@ -7,14 +7,20 @@ import {
7
7
  classifyTrajectory,
8
8
  cleanupGateProxyConfigs,
9
9
  cleanupMcpConfig,
10
+ cleanupWorktree,
10
11
  composeStuckDetectionPrompt,
12
+ defaultWorktreeRoot,
11
13
  detectBranchMerged,
14
+ ensureWorktreeRemote,
12
15
  generateGateProxyMcpConfig,
13
16
  generateMcpConfig,
17
+ generateWorktreeName,
18
+ mergeWorktreeBranch,
14
19
  parseJsonFromLlmOutput,
15
20
  parseStuckDetectionResponse,
16
- reapOrphanedMcpConfigDirs
17
- } from "./chunk-X2CJ3ZAI.js";
21
+ reapOrphanedMcpConfigDirs,
22
+ renderWorktreePath
23
+ } from "./chunk-3A4B7CUH.js";
18
24
  import {
19
25
  LoopCheckpointSchema,
20
26
  VerdictSchema,
@@ -34,8 +40,8 @@ import {
34
40
  // src/commands/pipeline.ts
35
41
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync3, unlinkSync as unlinkSync3, existsSync as existsSync3, appendFileSync as appendFileSync2, promises as fsPromises } from "fs";
36
42
  import { execFile } from "child_process";
37
- import { homedir as homedir3 } from "os";
38
- import { join as join4, dirname as dirname2, resolve, sep } from "path";
43
+ import { homedir as homedir2 } from "os";
44
+ import { join as join3, dirname as dirname2, resolve, sep } from "path";
39
45
  import { promisify } from "util";
40
46
 
41
47
  // src/lib/harness-signal.ts
@@ -58,150 +64,6 @@ function resolveToolRestrictions(builtinTools, allowedTools, disallowedTools) {
58
64
  };
59
65
  }
60
66
 
61
- // src/lib/worktree.ts
62
- import { execFile as defaultExecFile, execFileSync } from "child_process";
63
- import { homedir } from "os";
64
- import { join } from "path";
65
- function generateWorktreeName(ticketNumber, columnName) {
66
- const slug = columnSlug(columnName);
67
- return `kantban-${ticketNumber}-${slug}`;
68
- }
69
- function columnSlug(columnName) {
70
- return columnName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
71
- }
72
- function renderWorktreePath(ticketNumber, columnName, pathPattern, options) {
73
- const slug = columnSlug(columnName);
74
- const worktreeName = generateWorktreeName(ticketNumber, columnName);
75
- if (pathPattern) {
76
- return pathPattern.replace(/\{ticket_number\}/g, String(ticketNumber)).replace(/\{column_slug\}/g, slug).replace(/\{worktree_name\}/g, worktreeName);
77
- }
78
- return join(options.defaultRoot, options.boardId, slug, String(ticketNumber));
79
- }
80
- function defaultWorktreeRoot() {
81
- return join(homedir(), ".kantban", "worktrees");
82
- }
83
- function isPlausibleRemoteUrl(url) {
84
- return url.includes("/") || url.includes("@") || url.includes("://");
85
- }
86
- function ensureWorktreeRemote(worktreePath) {
87
- try {
88
- const remotes = normalizeEol(execFileSync("git", ["-C", worktreePath, "remote"], {
89
- stdio: "pipe",
90
- encoding: "utf-8"
91
- })).trim();
92
- if (remotes.split("\n").includes("origin")) {
93
- const currentUrl = normalizeEol(execFileSync("git", ["-C", worktreePath, "remote", "get-url", "origin"], {
94
- stdio: "pipe",
95
- encoding: "utf-8"
96
- })).trim();
97
- if (isPlausibleRemoteUrl(currentUrl)) return;
98
- console.error(`[worktree] Invalid origin URL in ${worktreePath}: "${currentUrl}" \u2014 fixing`);
99
- execFileSync("git", ["-C", worktreePath, "remote", "remove", "origin"], {
100
- stdio: "pipe"
101
- });
102
- }
103
- const originUrl = normalizeEol(execFileSync("git", ["remote", "get-url", "origin"], {
104
- stdio: "pipe",
105
- encoding: "utf-8"
106
- })).trim();
107
- if (originUrl && isPlausibleRemoteUrl(originUrl)) {
108
- execFileSync("git", ["-C", worktreePath, "remote", "add", "origin", originUrl], {
109
- stdio: "pipe"
110
- });
111
- console.error(`[worktree] Added missing origin remote to ${worktreePath}: ${originUrl}`);
112
- } else {
113
- console.error(`[worktree] WARNING: main repo origin URL is also invalid: "${originUrl}"`);
114
- }
115
- } catch (err) {
116
- const msg = err instanceof Error ? err.message : String(err);
117
- console.error(`[worktree] Failed to ensure remote for ${worktreePath}: ${msg}`);
118
- }
119
- }
120
- async function cleanupWorktree(worktreeName, exec = defaultExecFile) {
121
- let target = worktreeName;
122
- try {
123
- const path = await findWorktreeForBranch(exec, worktreeName);
124
- if (!path) return true;
125
- target = path;
126
- } catch {
127
- return true;
128
- }
129
- return new Promise((resolve2) => {
130
- exec("git", ["worktree", "remove", "--force", target], (err) => {
131
- if (err) {
132
- console.error(`[worktree] cleanup failed for ${worktreeName} (${target}): ${err.message}`);
133
- resolve2(false);
134
- } else {
135
- resolve2(true);
136
- }
137
- });
138
- });
139
- }
140
- function execPromise(exec, cmd, args) {
141
- return new Promise((resolve2, reject) => {
142
- exec(cmd, args, (err, stdout, stderr) => {
143
- if (err) reject(Object.assign(err, { stdout, stderr }));
144
- else resolve2({ stdout, stderr });
145
- });
146
- });
147
- }
148
- async function findWorktreeForBranch(exec, branch) {
149
- try {
150
- const { stdout } = await execPromise(exec, "git", ["worktree", "list", "--porcelain"]);
151
- const targetRef = `refs/heads/${branch}`;
152
- let currentPath = null;
153
- for (const line of normalizeEol(stdout).split("\n")) {
154
- if (line.startsWith("worktree ")) currentPath = line.slice("worktree ".length);
155
- if (line.startsWith("branch ") && line.slice("branch ".length) === targetRef && currentPath) {
156
- return currentPath;
157
- }
158
- }
159
- return null;
160
- } catch {
161
- return null;
162
- }
163
- }
164
- async function mergeWorktreeBranch(worktreeName, integrationBranch, exec = defaultExecFile) {
165
- try {
166
- try {
167
- await execPromise(exec, "git", ["rev-parse", "--verify", worktreeName]);
168
- } catch {
169
- return true;
170
- }
171
- await execPromise(exec, "git", ["branch", integrationBranch, "HEAD"]).catch(() => {
172
- });
173
- const checkedOutPath = await findWorktreeForBranch(exec, integrationBranch);
174
- if (checkedOutPath) {
175
- await execPromise(exec, "git", ["-C", checkedOutPath, "merge", "--no-edit", worktreeName]);
176
- console.error(`[worktree] merged ${worktreeName} \u2192 ${integrationBranch}`);
177
- return true;
178
- }
179
- const { stdout: baseOut } = await execPromise(exec, "git", ["merge-base", integrationBranch, worktreeName]);
180
- const mergeBase = baseOut.trim();
181
- const { stdout: integrationSha } = await execPromise(exec, "git", ["rev-parse", integrationBranch]);
182
- if (integrationSha.trim() === mergeBase) {
183
- const { stdout: worktreeSha } = await execPromise(exec, "git", ["rev-parse", worktreeName]);
184
- await execPromise(exec, "git", ["update-ref", `refs/heads/${integrationBranch}`, worktreeSha.trim()]);
185
- console.error(`[worktree] fast-forward merged ${worktreeName} \u2192 ${integrationBranch}`);
186
- return true;
187
- }
188
- const tmpWorktree = `merge-tmp-${Date.now()}`;
189
- try {
190
- await execPromise(exec, "git", ["worktree", "add", tmpWorktree, integrationBranch]);
191
- await execPromise(exec, "git", ["-C", tmpWorktree, "merge", "--no-edit", worktreeName]);
192
- console.error(`[worktree] merged ${worktreeName} \u2192 ${integrationBranch}`);
193
- } finally {
194
- await execPromise(exec, "git", ["worktree", "remove", "--force", tmpWorktree]).catch(() => {
195
- });
196
- }
197
- return true;
198
- } catch (err) {
199
- const msg = err instanceof Error ? err.message : String(err);
200
- console.error(`[worktree] merge failed for ${worktreeName} \u2192 ${integrationBranch}: ${msg}`);
201
- return false;
202
- }
203
- }
204
-
205
67
  // src/lib/constraint-evaluator.ts
206
68
  function resolveColumn(board, columnId, subjectRef) {
207
69
  const ref = subjectRef ?? "self";
@@ -2780,7 +2642,7 @@ var PipelineWsClient = class _PipelineWsClient {
2780
2642
 
2781
2643
  // src/lib/logger.ts
2782
2644
  import { mkdirSync, writeFileSync, appendFileSync, readdirSync, rmSync, statSync, renameSync } from "fs";
2783
- import { join as join2 } from "path";
2645
+ import { join } from "path";
2784
2646
  var MAX_LOG_BYTES = 5 * 1024 * 1024;
2785
2647
  var MAX_ROTATED_FILES = 3;
2786
2648
  var ROTATION_CHECK_INTERVAL = 100;
@@ -2788,11 +2650,11 @@ var PipelineLogger = class {
2788
2650
  boardDir;
2789
2651
  writeCount = 0;
2790
2652
  constructor(baseDir, boardId) {
2791
- this.boardDir = join2(baseDir, boardId);
2653
+ this.boardDir = join(baseDir, boardId);
2792
2654
  mkdirSync(this.boardDir, { recursive: true });
2793
2655
  }
2794
2656
  orchestrator(message) {
2795
- const logPath = join2(this.boardDir, "orchestrator.log");
2657
+ const logPath = join(this.boardDir, "orchestrator.log");
2796
2658
  const entry = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
2797
2659
  `;
2798
2660
  appendFileSync(logPath, entry);
@@ -2824,10 +2686,10 @@ var PipelineLogger = class {
2824
2686
  }
2825
2687
  }
2826
2688
  iteration(ticketNumber, iterationNum, data) {
2827
- const ticketDir = join2(this.boardDir, ticketNumber);
2689
+ const ticketDir = join(this.boardDir, ticketNumber);
2828
2690
  mkdirSync(ticketDir, { recursive: true });
2829
2691
  const padded = String(iterationNum).padStart(3, "0");
2830
- const logPath = join2(ticketDir, `iteration-${padded}.log`);
2692
+ const logPath = join(ticketDir, `iteration-${padded}.log`);
2831
2693
  const entry = {
2832
2694
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2833
2695
  iteration: iterationNum,
@@ -2846,7 +2708,7 @@ var PipelineLogger = class {
2846
2708
  try {
2847
2709
  const entries = readdirSync(this.boardDir, { withFileTypes: true });
2848
2710
  for (const entry of entries) {
2849
- const fullPath = join2(this.boardDir, entry.name);
2711
+ const fullPath = join(this.boardDir, entry.name);
2850
2712
  if (entry.isDirectory() && entry.name !== ".") {
2851
2713
  try {
2852
2714
  const stat = statSync(fullPath);
@@ -3483,7 +3345,7 @@ var ProviderRegistry = class {
3483
3345
  };
3484
3346
 
3485
3347
  // src/providers/codex-provider.ts
3486
- import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
3348
+ import { spawn as spawn2, execFileSync } from "child_process";
3487
3349
  import { existsSync, rmSync as rmSync2 } from "fs";
3488
3350
 
3489
3351
  // src/providers/codex-jsonl-parser.ts
@@ -3624,12 +3486,12 @@ var CodexProvider = class {
3624
3486
  const branch = request.branch ?? request.workingDirectory;
3625
3487
  if (!existsSync(request.workingDirectory)) {
3626
3488
  try {
3627
- execFileSync2("git", ["worktree", "add", "-b", branch, request.workingDirectory, "HEAD"], {
3489
+ execFileSync("git", ["worktree", "add", "-b", branch, request.workingDirectory, "HEAD"], {
3628
3490
  stdio: "pipe"
3629
3491
  });
3630
3492
  } catch {
3631
3493
  try {
3632
- execFileSync2("git", ["worktree", "add", request.workingDirectory, branch], {
3494
+ execFileSync("git", ["worktree", "add", request.workingDirectory, branch], {
3633
3495
  stdio: "pipe"
3634
3496
  });
3635
3497
  } catch (err) {
@@ -3639,18 +3501,18 @@ var CodexProvider = class {
3639
3501
  }
3640
3502
  } else {
3641
3503
  try {
3642
- execFileSync2("git", ["-C", request.workingDirectory, "rev-parse", "--git-dir"], {
3504
+ execFileSync("git", ["-C", request.workingDirectory, "rev-parse", "--git-dir"], {
3643
3505
  stdio: "pipe"
3644
3506
  });
3645
3507
  } catch {
3646
3508
  try {
3647
3509
  rmSync2(request.workingDirectory, { recursive: true, force: true });
3648
- execFileSync2("git", ["worktree", "add", "-b", branch, request.workingDirectory, "HEAD"], {
3510
+ execFileSync("git", ["worktree", "add", "-b", branch, request.workingDirectory, "HEAD"], {
3649
3511
  stdio: "pipe"
3650
3512
  });
3651
3513
  } catch {
3652
3514
  try {
3653
- execFileSync2("git", ["worktree", "add", request.workingDirectory, branch], {
3515
+ execFileSync("git", ["worktree", "add", request.workingDirectory, branch], {
3654
3516
  stdio: "pipe"
3655
3517
  });
3656
3518
  } catch (err) {
@@ -3670,6 +3532,12 @@ var CodexProvider = class {
3670
3532
  const child = spawn2(cmd, resolvedArgs, {
3671
3533
  stdio: ["ignore", "pipe", "pipe"],
3672
3534
  env: { ...process.env, ...env },
3535
+ // Run the agent process *inside* its worktree, not the orchestrator's
3536
+ // launch dir. Codex's `-C` arg alone is not enough: without an explicit
3537
+ // cwd the process inherits the orchestrator's cwd (the main checkout),
3538
+ // and any shell command that resolves to process.cwd rather than `-C`
3539
+ // corrupts the shared repo (detached HEAD, stray writes).
3540
+ ...request.workingDirectory ? { cwd: request.workingDirectory } : {},
3673
3541
  ...prefixArgs.length > 0 ? {} : crossSpawnOptions()
3674
3542
  });
3675
3543
  const parser = new CodexJsonlParser();
@@ -3786,10 +3654,10 @@ var CodexProvider = class {
3786
3654
  };
3787
3655
 
3788
3656
  // src/providers/gemini-provider.ts
3789
- import { spawn as spawn3, execFileSync as execFileSync3 } from "child_process";
3657
+ import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
3790
3658
  import { existsSync as existsSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, rmSync as rmSync3 } from "fs";
3791
- import { join as join3, dirname } from "path";
3792
- import { homedir as homedir2 } from "os";
3659
+ import { join as join2, dirname } from "path";
3660
+ import { homedir } from "os";
3793
3661
  import { fileURLToPath } from "url";
3794
3662
  import { readFileSync as readFileSync2 } from "fs";
3795
3663
 
@@ -3952,13 +3820,13 @@ function resolveHookScriptPath() {
3952
3820
  try {
3953
3821
  const thisDir = dirname(fileURLToPath(import.meta.url));
3954
3822
  const candidates = [
3955
- join3(thisDir, "lib", "gemini-hooks.mjs"),
3823
+ join2(thisDir, "lib", "gemini-hooks.mjs"),
3956
3824
  // dist/lib/ (flat bundle)
3957
- join3(thisDir, "..", "lib", "gemini-hooks.mjs"),
3825
+ join2(thisDir, "..", "lib", "gemini-hooks.mjs"),
3958
3826
  // dist/../lib/ (nested)
3959
- join3(thisDir, "..", "src", "lib", "gemini-hooks.mjs"),
3827
+ join2(thisDir, "..", "src", "lib", "gemini-hooks.mjs"),
3960
3828
  // source from dist/
3961
- join3(thisDir, "..", "..", "src", "lib", "gemini-hooks.mjs")
3829
+ join2(thisDir, "..", "..", "src", "lib", "gemini-hooks.mjs")
3962
3830
  // source from dist/providers/
3963
3831
  ];
3964
3832
  for (const p of candidates) {
@@ -3998,12 +3866,12 @@ var GeminiProvider = class _GeminiProvider {
3998
3866
  if (request.workingDirectory) {
3999
3867
  if (!existsSync2(request.workingDirectory)) {
4000
3868
  try {
4001
- execFileSync3("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
3869
+ execFileSync2("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
4002
3870
  stdio: "pipe"
4003
3871
  });
4004
3872
  } catch {
4005
3873
  try {
4006
- execFileSync3("git", ["worktree", "add", request.workingDirectory, request.workingDirectory], {
3874
+ execFileSync2("git", ["worktree", "add", request.workingDirectory, request.workingDirectory], {
4007
3875
  stdio: "pipe"
4008
3876
  });
4009
3877
  } catch {
@@ -4012,18 +3880,18 @@ var GeminiProvider = class _GeminiProvider {
4012
3880
  }
4013
3881
  } else {
4014
3882
  try {
4015
- execFileSync3("git", ["-C", request.workingDirectory, "rev-parse", "--git-dir"], {
3883
+ execFileSync2("git", ["-C", request.workingDirectory, "rev-parse", "--git-dir"], {
4016
3884
  stdio: "pipe"
4017
3885
  });
4018
3886
  } catch {
4019
3887
  try {
4020
3888
  rmSync3(request.workingDirectory, { recursive: true, force: true });
4021
- execFileSync3("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
3889
+ execFileSync2("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
4022
3890
  stdio: "pipe"
4023
3891
  });
4024
3892
  } catch {
4025
3893
  try {
4026
- execFileSync3("git", ["worktree", "add", request.workingDirectory, request.workingDirectory], {
3894
+ execFileSync2("git", ["worktree", "add", request.workingDirectory, request.workingDirectory], {
4027
3895
  stdio: "pipe"
4028
3896
  });
4029
3897
  } catch {
@@ -4152,9 +4020,9 @@ var GeminiProvider = class _GeminiProvider {
4152
4020
  }
4153
4021
  writeGeminiSettings(request, degraded) {
4154
4022
  try {
4155
- const baseDir = join3(homedir2(), ".kantban", "pipelines", "gemini-tmp");
4156
- const dir = join3(baseDir, `session-${Date.now()}`);
4157
- const geminiDir = join3(dir, ".gemini");
4023
+ const baseDir = join2(homedir(), ".kantban", "pipelines", "gemini-tmp");
4024
+ const dir = join2(baseDir, `session-${Date.now()}`);
4025
+ const geminiDir = join2(dir, ".gemini");
4158
4026
  mkdirSync2(geminiDir, { recursive: true, mode: 448 });
4159
4027
  const settings = {};
4160
4028
  if (request.mcpConfig && Object.keys(request.mcpConfig.servers).length > 0) {
@@ -4178,20 +4046,20 @@ var GeminiProvider = class _GeminiProvider {
4178
4046
  hookConfig.disallowedTools = tr.disallowedTools ? translateToolNames(tr.disallowedTools) : null;
4179
4047
  if (tr.tools !== void 0) hookConfig.builtinToolsMode = tr.tools;
4180
4048
  const ext = IS_WINDOWS ? ".cmd" : ".sh";
4181
- const wrapperPath = join3(dir, `.kantban-hook-before-tool${ext}`);
4182
- writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "BeforeToolSelection", join3(dir, ".kantban-hook-config.json")), { mode: 493 });
4049
+ const wrapperPath = join2(dir, `.kantban-hook-before-tool${ext}`);
4050
+ writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "BeforeToolSelection", join2(dir, ".kantban-hook-config.json")), { mode: 493 });
4183
4051
  hooks.BeforeToolSelection = [{ matcher: "*", hooks: [{ type: "command", command: wrapperPath, timeout: 3e4 }] }];
4184
4052
  }
4185
4053
  if (request.maxTurns && request.mcpConfig) {
4186
4054
  hookConfig.maxTurns = request.maxTurns;
4187
- hookConfig.turnFile = join3(dir, ".kantban-turn-counter");
4055
+ hookConfig.turnFile = join2(dir, ".kantban-turn-counter");
4188
4056
  const ext = IS_WINDOWS ? ".cmd" : ".sh";
4189
- const wrapperPath = join3(dir, `.kantban-hook-after-agent${ext}`);
4190
- writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "AfterAgent", join3(dir, ".kantban-hook-config.json")), { mode: 493 });
4057
+ const wrapperPath = join2(dir, `.kantban-hook-after-agent${ext}`);
4058
+ writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "AfterAgent", join2(dir, ".kantban-hook-config.json")), { mode: 493 });
4191
4059
  hooks.AfterAgent = [{ matcher: "*", hooks: [{ type: "command", command: wrapperPath, timeout: 3e4 }] }];
4192
4060
  }
4193
4061
  if (Object.keys(hookConfig).length > 0) {
4194
- writeFileSync3(join3(dir, ".kantban-hook-config.json"), JSON.stringify(hookConfig), { mode: 384 });
4062
+ writeFileSync3(join2(dir, ".kantban-hook-config.json"), JSON.stringify(hookConfig), { mode: 384 });
4195
4063
  }
4196
4064
  if (Object.keys(hooks).length > 0) {
4197
4065
  settings.hooks = hooks;
@@ -4204,7 +4072,7 @@ var GeminiProvider = class _GeminiProvider {
4204
4072
  process.stderr.write(`[gemini] Hook script not found \u2014 tool scoping and turn limits unavailable
4205
4073
  `);
4206
4074
  }
4207
- writeFileSync3(join3(geminiDir, "settings.json"), JSON.stringify(settings, null, 2), { mode: 384 });
4075
+ writeFileSync3(join2(geminiDir, "settings.json"), JSON.stringify(settings, null, 2), { mode: 384 });
4208
4076
  return dir;
4209
4077
  } catch (err) {
4210
4078
  process.stderr.write(`[gemini] Failed to write settings.json: ${err}
@@ -4270,11 +4138,11 @@ node "${hookScript}" "${event}" "${configFile}" < "$STDIN_FILE"
4270
4138
  }
4271
4139
  copySettingsToDir(settingsDir, targetDir) {
4272
4140
  try {
4273
- const sourceFile = join3(settingsDir, ".gemini", "settings.json");
4274
- const targetGeminiDir = join3(targetDir, ".gemini");
4141
+ const sourceFile = join2(settingsDir, ".gemini", "settings.json");
4142
+ const targetGeminiDir = join2(targetDir, ".gemini");
4275
4143
  mkdirSync2(targetGeminiDir, { recursive: true, mode: 448 });
4276
4144
  const content = readFileSync2(sourceFile, "utf-8");
4277
- writeFileSync3(join3(targetGeminiDir, "settings.json"), content, { mode: 384 });
4145
+ writeFileSync3(join2(targetGeminiDir, "settings.json"), content, { mode: 384 });
4278
4146
  } catch {
4279
4147
  }
4280
4148
  }
@@ -4414,10 +4282,10 @@ Flags:
4414
4282
  return { boardId, once, dryRun, columnFilter, maxIterations, maxBudget, model, provider, concurrency, logRetention, yes };
4415
4283
  }
4416
4284
  function pidDir(boardId) {
4417
- return join4(homedir3(), ".kantban", "pipelines", boardId);
4285
+ return join3(homedir2(), ".kantban", "pipelines", boardId);
4418
4286
  }
4419
4287
  function pidFilePath(boardId) {
4420
- return join4(pidDir(boardId), "orchestrator.pid");
4288
+ return join3(pidDir(boardId), "orchestrator.pid");
4421
4289
  }
4422
4290
  function writePidFile(boardId) {
4423
4291
  const dir = pidDir(boardId);
@@ -4431,7 +4299,7 @@ function removePidFile(boardId) {
4431
4299
  }
4432
4300
  }
4433
4301
  function childManifestPath(boardId) {
4434
- return join4(pidDir(boardId), "children.pid");
4302
+ return join3(pidDir(boardId), "children.pid");
4435
4303
  }
4436
4304
  function readChildManifest(boardId) {
4437
4305
  try {
@@ -4476,7 +4344,7 @@ function cleanupOrphanedProcesses(boardId) {
4476
4344
  console.log(`Killed ${String(manifestPids.length)} orphaned child process(es) from manifest`);
4477
4345
  removeChildManifest(boardId);
4478
4346
  }
4479
- const staleReaperPath = join4(pidDir(boardId), "reaper.pid");
4347
+ const staleReaperPath = join3(pidDir(boardId), "reaper.pid");
4480
4348
  try {
4481
4349
  if (existsSync3(staleReaperPath)) {
4482
4350
  const reaperPid = parseInt(readFileSync3(staleReaperPath, "utf-8").trim(), 10);
@@ -4535,7 +4403,7 @@ async function runPipeline(client, args) {
4535
4403
  return;
4536
4404
  }
4537
4405
  }
4538
- const gateFilePath = join4(process.cwd(), "pipeline.gates.yaml");
4406
+ const gateFilePath = join3(process.cwd(), "pipeline.gates.yaml");
4539
4407
  let gateConfig;
4540
4408
  if (!existsSync3(gateFilePath)) {
4541
4409
  console.error(`Error: pipeline.gates.yaml not found in ${process.cwd()}`);
@@ -4572,7 +4440,7 @@ async function runPipeline(client, args) {
4572
4440
  ...opts.provider ? { intelligence_provider: opts.provider } : {}
4573
4441
  };
4574
4442
  const intelligenceProvider = registry.resolveForIntelligence(boardProviderConfig);
4575
- const logBaseDir = join4(homedir3(), ".kantban", "pipelines");
4443
+ const logBaseDir = join3(homedir2(), ".kantban", "pipelines");
4576
4444
  const logger = new PipelineLogger(logBaseDir, opts.boardId);
4577
4445
  logger.pruneOldLogs(opts.logRetention);
4578
4446
  let runMemory = null;
@@ -4636,7 +4504,21 @@ async function runPipeline(client, args) {
4636
4504
  effectiveConfig.model = registry.resolveModel(columnProvider, effectiveConfig.model);
4637
4505
  }
4638
4506
  const { gates: columnGates } = resolveGatesForColumn(gateConfig, resolvedColumnName);
4639
- const gateCwd = effectiveConfig.worktreePath ?? (effectiveConfig.worktreeName ? join4(process.cwd(), effectiveConfig.worktreeName) : process.cwd());
4507
+ const gateCwd = effectiveConfig.worktreePath ?? (effectiveConfig.worktreeName ? join3(process.cwd(), effectiveConfig.worktreeName) : process.cwd());
4508
+ if (effectiveConfig.worktreePath || effectiveConfig.worktreeName) {
4509
+ const primaryRepoRoot = await getPrimaryRepoRoot();
4510
+ const resolvedGate = resolve(gateCwd);
4511
+ const insideRepo = resolvedGate === primaryRepoRoot || resolvedGate.startsWith(primaryRepoRoot + sep) || primaryRepoRoot.startsWith(resolvedGate + sep);
4512
+ if (insideRepo) {
4513
+ const errMsg = `[fatal] dispatch refused: gate cwd "${resolvedGate}" is inside the primary repo "${primaryRepoRoot}" \u2014 refusing to run gates against the shared checkout. Fix agent_config.worktree.path_pattern.`;
4514
+ logger.orchestrator(errMsg);
4515
+ await deps.createComment(
4516
+ ticketId,
4517
+ `[dispatch] failed: gate cwd would be the primary repo ("${resolvedGate}"). Fix worktree.path_pattern; the orchestrator will retry.`
4518
+ );
4519
+ return { reason: "error", iterations: 0, gutterCount: 0, lastError: errMsg };
4520
+ }
4521
+ }
4640
4522
  const effectiveMcpConfigPath = columnGates.length > 0 ? generateGateProxyMcpConfig(
4641
4523
  client.baseUrl,
4642
4524
  client.token,
@@ -5145,7 +5027,7 @@ Received ${signal}. Shutting down gracefully...`);
5145
5027
  cleanupOrphanedProcesses(opts.boardId);
5146
5028
  writePidFile(opts.boardId);
5147
5029
  logger.orchestrator(`PID file written: ${String(process.pid)}`);
5148
- const reaperPidPath = join4(pidDir(opts.boardId), "reaper.pid");
5030
+ const reaperPidPath = join3(pidDir(opts.boardId), "reaper.pid");
5149
5031
  const reaperProcess = spawnReaper({
5150
5032
  orchestratorPid: process.pid,
5151
5033
  manifestPath: childManifestPath(opts.boardId),
@@ -5335,4 +5217,4 @@ export {
5335
5217
  runPipeline,
5336
5218
  stopPipeline
5337
5219
  };
5338
- //# sourceMappingURL=pipeline-6J64Z7VH.js.map
5220
+ //# sourceMappingURL=pipeline-KKUWLU3L.js.map