kantban-cli 0.1.24 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -31,10 +31,10 @@ async function main() {
31
31
  }
32
32
  case "pipeline": {
33
33
  if (args[0] === "stop") {
34
- const { stopPipeline } = await import("./pipeline-TEE6GBNZ.js");
34
+ const { stopPipeline } = await import("./pipeline-IYKEQZWN.js");
35
35
  await stopPipeline(args.slice(1));
36
36
  } else {
37
- const { runPipeline } = await import("./pipeline-TEE6GBNZ.js");
37
+ const { runPipeline } = await import("./pipeline-IYKEQZWN.js");
38
38
  await runPipeline(client, args);
39
39
  }
40
40
  break;
@@ -22,9 +22,9 @@ import {
22
22
  } from "./chunk-ZTQJMXJM.js";
23
23
 
24
24
  // src/commands/pipeline.ts
25
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3, readFileSync as readFileSync2, unlinkSync as unlinkSync2, existsSync as existsSync2, appendFileSync as appendFileSync2 } from "fs";
26
- import { homedir } from "os";
27
- import { join as join2 } from "path";
25
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync3, unlinkSync as unlinkSync3, existsSync as existsSync3, appendFileSync as appendFileSync2 } from "fs";
26
+ import { homedir as homedir2 } from "os";
27
+ import { join as join3 } from "path";
28
28
 
29
29
  // src/lib/tool-profiles.ts
30
30
  function resolveToolRestrictions(builtinTools, allowedTools, disallowedTools) {
@@ -3448,15 +3448,433 @@ var CodexProvider = class {
3448
3448
  }
3449
3449
  };
3450
3450
 
3451
+ // src/providers/gemini-provider.ts
3452
+ import { spawn as spawn3, execFileSync as execFileSync3 } from "child_process";
3453
+ import { existsSync as existsSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
3454
+ import { join as join2, dirname } from "path";
3455
+ import { homedir } from "os";
3456
+ import { fileURLToPath } from "url";
3457
+ import { readFileSync as readFileSync2 } from "fs";
3458
+
3459
+ // src/providers/gemini-jsonl-parser.ts
3460
+ var GeminiJsonlParser = class {
3461
+ buffer = "";
3462
+ /** Accumulates lines that failed individual JSON parse (multi-line object) */
3463
+ jsonAccum = "";
3464
+ toolCallCount = 0;
3465
+ inputTokens = 0;
3466
+ outputTokens = 0;
3467
+ lastOutput = "";
3468
+ onEvent = () => {
3469
+ };
3470
+ onError = () => {
3471
+ };
3472
+ feed(chunk) {
3473
+ this.buffer += chunk;
3474
+ const lines = this.buffer.split("\n");
3475
+ this.buffer = lines.pop() ?? "";
3476
+ for (const line of lines) {
3477
+ this.parseLine(line.trim());
3478
+ }
3479
+ }
3480
+ flush() {
3481
+ const trimmed = this.buffer.trim();
3482
+ this.buffer = "";
3483
+ if (trimmed) this.parseLine(trimmed);
3484
+ this.tryFlushAccum();
3485
+ }
3486
+ getToolCallCount() {
3487
+ return this.toolCallCount;
3488
+ }
3489
+ getUsage() {
3490
+ return { inputTokens: this.inputTokens, outputTokens: this.outputTokens };
3491
+ }
3492
+ getLastOutput() {
3493
+ return this.lastOutput;
3494
+ }
3495
+ reset() {
3496
+ this.buffer = "";
3497
+ this.toolCallCount = 0;
3498
+ this.inputTokens = 0;
3499
+ this.outputTokens = 0;
3500
+ this.lastOutput = "";
3501
+ }
3502
+ parseLine(line) {
3503
+ if (!line) return;
3504
+ if (this.jsonAccum) {
3505
+ this.jsonAccum += "\n" + line;
3506
+ if (this.tryFlushAccum()) return;
3507
+ return;
3508
+ }
3509
+ try {
3510
+ const raw = JSON.parse(line);
3511
+ this.translateEvent(raw);
3512
+ } catch {
3513
+ if (line.startsWith("{") || line.startsWith("[")) {
3514
+ this.jsonAccum = line;
3515
+ }
3516
+ }
3517
+ }
3518
+ /** Try to parse the accumulated multi-line buffer as JSON. Returns true if successful. */
3519
+ tryFlushAccum() {
3520
+ if (!this.jsonAccum) return false;
3521
+ try {
3522
+ const raw = JSON.parse(this.jsonAccum);
3523
+ this.jsonAccum = "";
3524
+ this.translateEvent(raw);
3525
+ return true;
3526
+ } catch {
3527
+ return false;
3528
+ }
3529
+ }
3530
+ translateEvent(raw) {
3531
+ const eventType = raw.type;
3532
+ if (eventType === "message") {
3533
+ if (raw.role === "assistant" && typeof raw.content === "string") {
3534
+ this.onEvent({ type: "text", text: raw.content });
3535
+ }
3536
+ } else if (eventType === "tool_use") {
3537
+ this.toolCallCount++;
3538
+ this.onEvent({
3539
+ type: "tool_call",
3540
+ tool: raw.name ?? "unknown",
3541
+ input: raw.args ?? raw.input
3542
+ });
3543
+ } else if (eventType === "tool_result") {
3544
+ this.onEvent({
3545
+ type: "tool_result",
3546
+ tool: raw.name ?? "unknown",
3547
+ output: raw.output ?? raw.result
3548
+ });
3549
+ } else if (eventType === "result" || raw.session_id && raw.stats) {
3550
+ const { inTok, outTok } = this.extractTokens(raw);
3551
+ this.inputTokens += inTok;
3552
+ this.outputTokens += outTok;
3553
+ if (inTok || outTok) {
3554
+ this.onEvent({ type: "usage", inputTokens: inTok, outputTokens: outTok });
3555
+ }
3556
+ const tools = raw.stats?.tools;
3557
+ if (tools?.totalCalls) this.toolCallCount = Math.max(this.toolCallCount, tools.totalCalls);
3558
+ if (typeof raw.response === "string") {
3559
+ this.lastOutput = raw.response;
3560
+ this.onEvent({ type: "done", result: raw.response });
3561
+ }
3562
+ } else if (eventType === "error") {
3563
+ const msg = raw.message ?? "unknown error";
3564
+ this.lastOutput = msg;
3565
+ this.onEvent({ type: "error", message: msg });
3566
+ }
3567
+ }
3568
+ /** Extract token counts from Gemini's nested stats structure */
3569
+ extractTokens(raw) {
3570
+ const stats = raw.stats;
3571
+ if (!stats) return { inTok: 0, outTok: 0 };
3572
+ if (stats.promptTokenCount || stats.input_tokens) {
3573
+ return {
3574
+ inTok: stats.promptTokenCount ?? stats.input_tokens ?? 0,
3575
+ outTok: stats.candidatesTokenCount ?? stats.output_tokens ?? 0
3576
+ };
3577
+ }
3578
+ if (stats.models && typeof stats.models === "object") {
3579
+ let inTok = 0;
3580
+ let outTok = 0;
3581
+ for (const model of Object.values(stats.models)) {
3582
+ const tokens = model?.tokens;
3583
+ if (tokens) {
3584
+ inTok += tokens.prompt ?? tokens.input ?? 0;
3585
+ outTok += tokens.candidates ?? tokens.output ?? 0;
3586
+ }
3587
+ }
3588
+ return { inTok, outTok };
3589
+ }
3590
+ return { inTok: 0, outTok: 0 };
3591
+ }
3592
+ };
3593
+
3594
+ // src/providers/gemini-provider.ts
3595
+ var GEMINI_TIMEOUT_MS = 60 * 60 * 1e3;
3596
+ var TOOL_NAME_MAP = {
3597
+ Write: "write_file",
3598
+ Edit: "replace",
3599
+ Read: "read_file",
3600
+ Bash: "run_shell_command",
3601
+ Grep: "grep_search",
3602
+ Glob: "glob",
3603
+ WebFetch: "web_fetch",
3604
+ WebSearch: "google_web_search",
3605
+ LS: "list_directory"
3606
+ };
3607
+ function translateToolNames(tools) {
3608
+ return tools.map((t) => TOOL_NAME_MAP[t] ?? t);
3609
+ }
3610
+ function resolveHookScriptPath() {
3611
+ try {
3612
+ const thisDir = dirname(fileURLToPath(import.meta.url));
3613
+ const candidates = [
3614
+ join2(thisDir, "lib", "gemini-hooks.mjs"),
3615
+ // dist/lib/ (flat bundle)
3616
+ join2(thisDir, "..", "lib", "gemini-hooks.mjs"),
3617
+ // dist/../lib/ (nested)
3618
+ join2(thisDir, "..", "src", "lib", "gemini-hooks.mjs"),
3619
+ // source from dist/
3620
+ join2(thisDir, "..", "..", "src", "lib", "gemini-hooks.mjs")
3621
+ // source from dist/providers/
3622
+ ];
3623
+ for (const p of candidates) {
3624
+ if (existsSync2(p)) return p;
3625
+ }
3626
+ return null;
3627
+ } catch {
3628
+ return null;
3629
+ }
3630
+ }
3631
+ var GeminiProvider = class {
3632
+ id = "gemini";
3633
+ displayName = "Gemini CLI";
3634
+ capabilities() {
3635
+ return {
3636
+ supportsToolAllowlist: true,
3637
+ supportsToolDenylist: true,
3638
+ supportsBuiltinToolStripping: true,
3639
+ supportsMaxTurns: true,
3640
+ supportsMcpConfigInjection: false,
3641
+ supportsMcpConfigOverride: true,
3642
+ supportsWorktreeFlag: false,
3643
+ supportsSandboxModes: true,
3644
+ supportedModels: [
3645
+ { id: "gemini-2.5-flash-lite", displayName: "Flash Lite", tier: "fast" },
3646
+ { id: "gemini-2.5-flash", displayName: "Flash", tier: "default" },
3647
+ { id: "gemini-2.5-pro", displayName: "Pro", tier: "thorough" }
3648
+ ],
3649
+ streamFormat: "jsonl"
3650
+ };
3651
+ }
3652
+ async invoke(request) {
3653
+ const degraded = [];
3654
+ const args = this.buildArgs(request);
3655
+ const startTime = Date.now();
3656
+ if (request.workingDirectory) {
3657
+ if (!existsSync2(request.workingDirectory)) {
3658
+ try {
3659
+ execFileSync3("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
3660
+ stdio: "pipe"
3661
+ });
3662
+ } catch {
3663
+ try {
3664
+ execFileSync3("git", ["worktree", "add", request.workingDirectory, request.workingDirectory], {
3665
+ stdio: "pipe"
3666
+ });
3667
+ } catch {
3668
+ degraded.push("worktreeCreation");
3669
+ }
3670
+ }
3671
+ } else {
3672
+ try {
3673
+ execFileSync3("git", ["-C", request.workingDirectory, "rev-parse", "--git-dir"], {
3674
+ stdio: "pipe"
3675
+ });
3676
+ } catch {
3677
+ try {
3678
+ execFileSync3("rm", ["-rf", request.workingDirectory], { stdio: "pipe" });
3679
+ execFileSync3("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
3680
+ stdio: "pipe"
3681
+ });
3682
+ } catch {
3683
+ try {
3684
+ execFileSync3("git", ["worktree", "add", request.workingDirectory, request.workingDirectory], {
3685
+ stdio: "pipe"
3686
+ });
3687
+ } catch {
3688
+ degraded.push("worktreeCreation");
3689
+ }
3690
+ }
3691
+ }
3692
+ }
3693
+ if (!degraded.includes("worktreeCreation") && existsSync2(request.workingDirectory)) {
3694
+ ensureWorktreeRemote(request.workingDirectory);
3695
+ }
3696
+ }
3697
+ const settingsDir = this.writeGeminiSettings(request, degraded);
3698
+ const cwd = request.workingDirectory ?? settingsDir;
3699
+ if (request.workingDirectory && settingsDir) {
3700
+ this.copySettingsToDir(settingsDir, request.workingDirectory);
3701
+ }
3702
+ return new Promise((resolve) => {
3703
+ const child = spawn3("gemini", args, {
3704
+ stdio: ["pipe", "pipe", "pipe"],
3705
+ cwd: cwd || void 0
3706
+ });
3707
+ const parser = new GeminiJsonlParser();
3708
+ parser.onEvent = (event) => request.onStreamEvent?.(event);
3709
+ parser.onError = (err) => process.stderr.write(`[gemini-jsonl] ${err.message}
3710
+ `);
3711
+ let stderr = "";
3712
+ child.stdout?.on("data", (chunk) => parser.feed(chunk.toString()));
3713
+ child.stderr?.on("data", (chunk) => {
3714
+ stderr += chunk.toString();
3715
+ });
3716
+ child.stdin?.end();
3717
+ if (request.abortSignal) {
3718
+ request.abortSignal.addEventListener("abort", () => {
3719
+ try {
3720
+ child.kill("SIGTERM");
3721
+ } catch {
3722
+ }
3723
+ }, { once: true });
3724
+ }
3725
+ let killTimer;
3726
+ const timeoutHandle = setTimeout(() => {
3727
+ try {
3728
+ child.kill("SIGTERM");
3729
+ } catch {
3730
+ }
3731
+ killTimer = setTimeout(() => {
3732
+ try {
3733
+ child.kill("SIGKILL");
3734
+ } catch {
3735
+ }
3736
+ }, 5e3);
3737
+ }, GEMINI_TIMEOUT_MS);
3738
+ let resolved = false;
3739
+ const finish = (code, errorMsg) => {
3740
+ if (resolved) return;
3741
+ resolved = true;
3742
+ clearTimeout(timeoutHandle);
3743
+ if (killTimer) clearTimeout(killTimer);
3744
+ parser.flush();
3745
+ const usage = parser.getUsage();
3746
+ const result = {
3747
+ exitCode: (code === 53 ? 0 : code) ?? 1,
3748
+ output: errorMsg ?? (parser.getLastOutput() || stderr),
3749
+ toolCallCount: parser.getToolCallCount(),
3750
+ usage,
3751
+ durationMs: Date.now() - startTime
3752
+ };
3753
+ if (degraded.length > 0) result.degradedCapabilities = degraded;
3754
+ resolve(result);
3755
+ };
3756
+ child.on("close", (code) => finish(code));
3757
+ child.on("error", (err) => finish(1, err.message));
3758
+ });
3759
+ }
3760
+ async preflight() {
3761
+ try {
3762
+ const whichModule = await import("which");
3763
+ const syncFn = whichModule.default?.sync ?? whichModule.sync;
3764
+ syncFn("gemini");
3765
+ return { available: true, authenticated: true };
3766
+ } catch {
3767
+ return { available: false, authenticated: false, error: "gemini binary not found on PATH" };
3768
+ }
3769
+ }
3770
+ buildArgs(request) {
3771
+ const args = [
3772
+ "-p",
3773
+ request.prompt,
3774
+ "--yolo",
3775
+ "--output-format",
3776
+ "json"
3777
+ ];
3778
+ if (request.model) args.push("--model", request.model);
3779
+ return args;
3780
+ }
3781
+ writeGeminiSettings(request, degraded) {
3782
+ try {
3783
+ const baseDir = join2(homedir(), ".kantban", "pipelines", "gemini-tmp");
3784
+ const dir = join2(baseDir, `session-${Date.now()}`);
3785
+ const geminiDir = join2(dir, ".gemini");
3786
+ mkdirSync2(geminiDir, { recursive: true, mode: 448 });
3787
+ const settings = {};
3788
+ if (request.mcpConfig && Object.keys(request.mcpConfig.servers).length > 0) {
3789
+ settings.mcpServers = {};
3790
+ for (const [name, server] of Object.entries(request.mcpConfig.servers)) {
3791
+ settings.mcpServers[name] = {
3792
+ command: server.command,
3793
+ args: server.args,
3794
+ env: server.env,
3795
+ trust: true
3796
+ };
3797
+ }
3798
+ }
3799
+ const hookPath = resolveHookScriptPath();
3800
+ if (hookPath) {
3801
+ const hooks = {};
3802
+ const hookConfig = {};
3803
+ if (request.toolRestrictions) {
3804
+ const tr = request.toolRestrictions;
3805
+ hookConfig.allowedTools = tr.allowedTools ? translateToolNames(tr.allowedTools) : null;
3806
+ hookConfig.disallowedTools = tr.disallowedTools ? translateToolNames(tr.disallowedTools) : null;
3807
+ if (tr.tools !== void 0) hookConfig.builtinToolsMode = tr.tools;
3808
+ const wrapperPath = join2(dir, ".kantban-hook-before-tool.sh");
3809
+ writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "BeforeToolSelection", join2(dir, ".kantban-hook-config.json")), { mode: 493 });
3810
+ hooks.BeforeToolSelection = [{ matcher: "*", hooks: [{ type: "command", command: wrapperPath, timeout: 3e4 }] }];
3811
+ }
3812
+ if (request.maxTurns) {
3813
+ hookConfig.maxTurns = request.maxTurns;
3814
+ hookConfig.turnFile = join2(dir, ".kantban-turn-counter");
3815
+ const wrapperPath = join2(dir, ".kantban-hook-after-agent.sh");
3816
+ writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "AfterAgent", join2(dir, ".kantban-hook-config.json")), { mode: 493 });
3817
+ hooks.AfterAgent = [{ matcher: "*", hooks: [{ type: "command", command: wrapperPath, timeout: 3e4 }] }];
3818
+ }
3819
+ if (Object.keys(hookConfig).length > 0) {
3820
+ writeFileSync3(join2(dir, ".kantban-hook-config.json"), JSON.stringify(hookConfig), { mode: 384 });
3821
+ }
3822
+ if (Object.keys(hooks).length > 0) {
3823
+ settings.hooks = hooks;
3824
+ }
3825
+ } else {
3826
+ if (request.maxTurns) degraded.push("maxTurns");
3827
+ if (request.toolRestrictions?.allowedTools?.length) degraded.push("toolAllowlist");
3828
+ if (request.toolRestrictions?.disallowedTools?.length) degraded.push("toolDenylist");
3829
+ if (request.toolRestrictions?.tools !== void 0) degraded.push("builtinToolStripping");
3830
+ process.stderr.write(`[gemini] Hook script not found \u2014 tool scoping and turn limits unavailable
3831
+ `);
3832
+ }
3833
+ writeFileSync3(join2(geminiDir, "settings.json"), JSON.stringify(settings, null, 2), { mode: 384 });
3834
+ return dir;
3835
+ } catch (err) {
3836
+ process.stderr.write(`[gemini] Failed to write settings.json: ${err}
3837
+ `);
3838
+ if (request.maxTurns) degraded.push("maxTurns");
3839
+ if (request.toolRestrictions?.allowedTools?.length) degraded.push("toolAllowlist");
3840
+ if (request.toolRestrictions?.disallowedTools?.length) degraded.push("toolDenylist");
3841
+ if (request.toolRestrictions?.tools !== void 0) degraded.push("builtinToolStripping");
3842
+ return null;
3843
+ }
3844
+ }
3845
+ /** Generate a per-session bash wrapper that invokes the Node hook script.
3846
+ * Gemini CLI ignores `args` in hook definitions, so we embed all arguments. */
3847
+ generateHookWrapper(hookScript, event, configFile) {
3848
+ return `#!/bin/bash
3849
+ # KantBan pipeline hook wrapper \u2014 Gemini CLI does not pass args, so we embed them.
3850
+ STDIN_FILE=$(mktemp)
3851
+ trap 'rm -f "$STDIN_FILE"' EXIT
3852
+ cat > "$STDIN_FILE"
3853
+ node "${hookScript}" "${event}" "${configFile}" < "$STDIN_FILE"
3854
+ `;
3855
+ }
3856
+ copySettingsToDir(settingsDir, targetDir) {
3857
+ try {
3858
+ const sourceFile = join2(settingsDir, ".gemini", "settings.json");
3859
+ const targetGeminiDir = join2(targetDir, ".gemini");
3860
+ mkdirSync2(targetGeminiDir, { recursive: true, mode: 448 });
3861
+ const content = readFileSync2(sourceFile, "utf-8");
3862
+ writeFileSync3(join2(targetGeminiDir, "settings.json"), content, { mode: 384 });
3863
+ } catch {
3864
+ }
3865
+ }
3866
+ };
3867
+
3451
3868
  // src/commands/pipeline.ts
3452
3869
  function createProviderRegistry() {
3453
3870
  const registry = new ProviderRegistry();
3454
3871
  registry.register(new ClaudeProvider());
3455
3872
  registry.register(new CodexProvider());
3873
+ registry.register(new GeminiProvider());
3456
3874
  return registry;
3457
3875
  }
3458
3876
  function readMcpConfigAsProviderConfig(filePath) {
3459
- const raw = JSON.parse(readFileSync2(filePath, "utf-8"));
3877
+ const raw = JSON.parse(readFileSync3(filePath, "utf-8"));
3460
3878
  return { servers: raw.mcpServers };
3461
3879
  }
3462
3880
  function parseArgs(args) {
@@ -3544,7 +3962,7 @@ Flags:
3544
3962
  --max-iterations <n> Override max iterations per ticket
3545
3963
  --max-budget <usd> Per-ticket budget cap (USD)
3546
3964
  --model <model> Override model preference
3547
- --provider <id> Override default provider (claude, codex)
3965
+ --provider <id> Override default provider (claude, codex, gemini)
3548
3966
  --concurrency <n> Override concurrency per column
3549
3967
  --log-retention <d> Log retention in days (default: 7)
3550
3968
  --yes, -y Skip safety confirmation`);
@@ -3553,28 +3971,28 @@ Flags:
3553
3971
  return { boardId, once, dryRun, columnFilter, maxIterations, maxBudget, model, provider, concurrency, logRetention, yes };
3554
3972
  }
3555
3973
  function pidDir(boardId) {
3556
- return join2(homedir(), ".kantban", "pipelines", boardId);
3974
+ return join3(homedir2(), ".kantban", "pipelines", boardId);
3557
3975
  }
3558
3976
  function pidFilePath(boardId) {
3559
- return join2(pidDir(boardId), "orchestrator.pid");
3977
+ return join3(pidDir(boardId), "orchestrator.pid");
3560
3978
  }
3561
3979
  function writePidFile(boardId) {
3562
3980
  const dir = pidDir(boardId);
3563
- mkdirSync2(dir, { recursive: true, mode: 448 });
3564
- writeFileSync3(pidFilePath(boardId), String(process.pid), { mode: 384 });
3981
+ mkdirSync3(dir, { recursive: true, mode: 448 });
3982
+ writeFileSync4(pidFilePath(boardId), String(process.pid), { mode: 384 });
3565
3983
  }
3566
3984
  function removePidFile(boardId) {
3567
3985
  try {
3568
- unlinkSync2(pidFilePath(boardId));
3986
+ unlinkSync3(pidFilePath(boardId));
3569
3987
  } catch {
3570
3988
  }
3571
3989
  }
3572
3990
  function childManifestPath(boardId) {
3573
- return join2(pidDir(boardId), "children.pid");
3991
+ return join3(pidDir(boardId), "children.pid");
3574
3992
  }
3575
3993
  function readChildManifest(boardId) {
3576
3994
  try {
3577
- const contents = readFileSync2(childManifestPath(boardId), "utf-8");
3995
+ const contents = readFileSync3(childManifestPath(boardId), "utf-8");
3578
3996
  return contents.split("\n").map((l) => l.trim()).filter((l) => l !== "").map(Number).filter((n) => !isNaN(n) && n > 0);
3579
3997
  } catch {
3580
3998
  return [];
@@ -3582,15 +4000,15 @@ function readChildManifest(boardId) {
3582
4000
  }
3583
4001
  function removeChildManifest(boardId) {
3584
4002
  try {
3585
- unlinkSync2(childManifestPath(boardId));
4003
+ unlinkSync3(childManifestPath(boardId));
3586
4004
  } catch {
3587
4005
  }
3588
4006
  }
3589
4007
  function cleanupOrphanedProcesses(boardId) {
3590
4008
  const pidPath = pidFilePath(boardId);
3591
- if (existsSync2(pidPath)) {
4009
+ if (existsSync3(pidPath)) {
3592
4010
  try {
3593
- const stalePid = parseInt(readFileSync2(pidPath, "utf-8").trim(), 10);
4011
+ const stalePid = parseInt(readFileSync3(pidPath, "utf-8").trim(), 10);
3594
4012
  if (stalePid && stalePid !== process.pid) {
3595
4013
  try {
3596
4014
  process.kill(stalePid, 0);
@@ -3615,10 +4033,10 @@ function cleanupOrphanedProcesses(boardId) {
3615
4033
  console.log(`Killed ${String(manifestPids.length)} orphaned child process(es) from manifest`);
3616
4034
  removeChildManifest(boardId);
3617
4035
  }
3618
- const staleReaperPath = join2(pidDir(boardId), "reaper.pid");
4036
+ const staleReaperPath = join3(pidDir(boardId), "reaper.pid");
3619
4037
  try {
3620
- if (existsSync2(staleReaperPath)) {
3621
- const reaperPid = parseInt(readFileSync2(staleReaperPath, "utf-8").trim(), 10);
4038
+ if (existsSync3(staleReaperPath)) {
4039
+ const reaperPid = parseInt(readFileSync3(staleReaperPath, "utf-8").trim(), 10);
3622
4040
  if (reaperPid && !isNaN(reaperPid)) {
3623
4041
  try {
3624
4042
  process.kill(reaperPid, 0);
@@ -3627,7 +4045,7 @@ function cleanupOrphanedProcesses(boardId) {
3627
4045
  } catch {
3628
4046
  }
3629
4047
  }
3630
- unlinkSync2(staleReaperPath);
4048
+ unlinkSync3(staleReaperPath);
3631
4049
  }
3632
4050
  } catch {
3633
4051
  }
@@ -3674,15 +4092,15 @@ async function runPipeline(client, args) {
3674
4092
  return;
3675
4093
  }
3676
4094
  }
3677
- const gateFilePath = join2(process.cwd(), "pipeline.gates.yaml");
4095
+ const gateFilePath = join3(process.cwd(), "pipeline.gates.yaml");
3678
4096
  let gateConfig;
3679
- if (!existsSync2(gateFilePath)) {
4097
+ if (!existsSync3(gateFilePath)) {
3680
4098
  console.error(`Error: pipeline.gates.yaml not found in ${process.cwd()}`);
3681
4099
  console.error('Run "kantban pipeline init" to generate a starter gate file.');
3682
4100
  process.exit(1);
3683
4101
  }
3684
4102
  try {
3685
- const gateYaml = readFileSync2(gateFilePath, "utf-8");
4103
+ const gateYaml = readFileSync3(gateFilePath, "utf-8");
3686
4104
  gateConfig = parseGateConfig(gateYaml);
3687
4105
  console.log(`Gate config loaded: ${gateConfig.default.length} default gate(s)`);
3688
4106
  } catch (err) {
@@ -3710,7 +4128,7 @@ async function runPipeline(client, args) {
3710
4128
  intelligence_provider: opts.provider ?? void 0
3711
4129
  };
3712
4130
  const intelligenceProvider = registry.resolveForIntelligence(boardProviderConfig);
3713
- const logBaseDir = join2(homedir(), ".kantban", "pipelines");
4131
+ const logBaseDir = join3(homedir2(), ".kantban", "pipelines");
3714
4132
  const logger = new PipelineLogger(logBaseDir, opts.boardId);
3715
4133
  logger.pruneOldLogs(opts.logRetention);
3716
4134
  let runMemory = null;
@@ -3747,7 +4165,7 @@ async function runPipeline(client, args) {
3747
4165
  effectiveConfig.model = registry.resolveModel(columnProvider, effectiveConfig.model);
3748
4166
  }
3749
4167
  const columnGates = resolveGatesForColumn(gateConfig, resolvedColumnName);
3750
- const gateCwd = effectiveConfig.worktreeName ? join2(process.cwd(), effectiveConfig.worktreeName) : void 0;
4168
+ const gateCwd = effectiveConfig.worktreeName ? join3(process.cwd(), effectiveConfig.worktreeName) : void 0;
3751
4169
  const effectiveMcpConfigPath = columnGates.length > 0 ? generateGateProxyMcpConfig(
3752
4170
  client.baseUrl,
3753
4171
  client.token,
@@ -4196,7 +4614,7 @@ Received ${signal}. Shutting down gracefully...`);
4196
4614
  cleanupOrphanedProcesses(opts.boardId);
4197
4615
  writePidFile(opts.boardId);
4198
4616
  logger.orchestrator(`PID file written: ${String(process.pid)}`);
4199
- const reaperPidPath = join2(pidDir(opts.boardId), "reaper.pid");
4617
+ const reaperPidPath = join3(pidDir(opts.boardId), "reaper.pid");
4200
4618
  const reaperProcess = spawnReaper({
4201
4619
  orchestratorPid: process.pid,
4202
4620
  manifestPath: childManifestPath(opts.boardId),
@@ -4359,7 +4777,7 @@ async function stopPipeline(args) {
4359
4777
  const pidFile = pidFilePath(boardId);
4360
4778
  let pid;
4361
4779
  try {
4362
- const content = readFileSync2(pidFile, "utf8").trim();
4780
+ const content = readFileSync3(pidFile, "utf8").trim();
4363
4781
  pid = Number(content);
4364
4782
  if (isNaN(pid) || pid <= 0) {
4365
4783
  throw new Error("Invalid PID");
@@ -4390,4 +4808,4 @@ export {
4390
4808
  runPipeline,
4391
4809
  stopPipeline
4392
4810
  };
4393
- //# sourceMappingURL=pipeline-TEE6GBNZ.js.map
4811
+ //# sourceMappingURL=pipeline-IYKEQZWN.js.map