kantban-cli 0.1.23 → 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-AM742SOH.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-AM742SOH.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) {
@@ -913,6 +913,7 @@ var PipelineOrchestrator = class {
913
913
  * Handle a pipeline event (typically from WebSocket via EventQueue).
914
914
  */
915
915
  async handleEvent(event) {
916
+ await this.refreshBoardScope();
916
917
  switch (event.type) {
917
918
  case "ticket:moved":
918
919
  case "ticket:created": {
@@ -3447,15 +3448,433 @@ var CodexProvider = class {
3447
3448
  }
3448
3449
  };
3449
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
+
3450
3868
  // src/commands/pipeline.ts
3451
3869
  function createProviderRegistry() {
3452
3870
  const registry = new ProviderRegistry();
3453
3871
  registry.register(new ClaudeProvider());
3454
3872
  registry.register(new CodexProvider());
3873
+ registry.register(new GeminiProvider());
3455
3874
  return registry;
3456
3875
  }
3457
3876
  function readMcpConfigAsProviderConfig(filePath) {
3458
- const raw = JSON.parse(readFileSync2(filePath, "utf-8"));
3877
+ const raw = JSON.parse(readFileSync3(filePath, "utf-8"));
3459
3878
  return { servers: raw.mcpServers };
3460
3879
  }
3461
3880
  function parseArgs(args) {
@@ -3543,7 +3962,7 @@ Flags:
3543
3962
  --max-iterations <n> Override max iterations per ticket
3544
3963
  --max-budget <usd> Per-ticket budget cap (USD)
3545
3964
  --model <model> Override model preference
3546
- --provider <id> Override default provider (claude, codex)
3965
+ --provider <id> Override default provider (claude, codex, gemini)
3547
3966
  --concurrency <n> Override concurrency per column
3548
3967
  --log-retention <d> Log retention in days (default: 7)
3549
3968
  --yes, -y Skip safety confirmation`);
@@ -3552,28 +3971,28 @@ Flags:
3552
3971
  return { boardId, once, dryRun, columnFilter, maxIterations, maxBudget, model, provider, concurrency, logRetention, yes };
3553
3972
  }
3554
3973
  function pidDir(boardId) {
3555
- return join2(homedir(), ".kantban", "pipelines", boardId);
3974
+ return join3(homedir2(), ".kantban", "pipelines", boardId);
3556
3975
  }
3557
3976
  function pidFilePath(boardId) {
3558
- return join2(pidDir(boardId), "orchestrator.pid");
3977
+ return join3(pidDir(boardId), "orchestrator.pid");
3559
3978
  }
3560
3979
  function writePidFile(boardId) {
3561
3980
  const dir = pidDir(boardId);
3562
- mkdirSync2(dir, { recursive: true, mode: 448 });
3563
- writeFileSync3(pidFilePath(boardId), String(process.pid), { mode: 384 });
3981
+ mkdirSync3(dir, { recursive: true, mode: 448 });
3982
+ writeFileSync4(pidFilePath(boardId), String(process.pid), { mode: 384 });
3564
3983
  }
3565
3984
  function removePidFile(boardId) {
3566
3985
  try {
3567
- unlinkSync2(pidFilePath(boardId));
3986
+ unlinkSync3(pidFilePath(boardId));
3568
3987
  } catch {
3569
3988
  }
3570
3989
  }
3571
3990
  function childManifestPath(boardId) {
3572
- return join2(pidDir(boardId), "children.pid");
3991
+ return join3(pidDir(boardId), "children.pid");
3573
3992
  }
3574
3993
  function readChildManifest(boardId) {
3575
3994
  try {
3576
- const contents = readFileSync2(childManifestPath(boardId), "utf-8");
3995
+ const contents = readFileSync3(childManifestPath(boardId), "utf-8");
3577
3996
  return contents.split("\n").map((l) => l.trim()).filter((l) => l !== "").map(Number).filter((n) => !isNaN(n) && n > 0);
3578
3997
  } catch {
3579
3998
  return [];
@@ -3581,15 +4000,15 @@ function readChildManifest(boardId) {
3581
4000
  }
3582
4001
  function removeChildManifest(boardId) {
3583
4002
  try {
3584
- unlinkSync2(childManifestPath(boardId));
4003
+ unlinkSync3(childManifestPath(boardId));
3585
4004
  } catch {
3586
4005
  }
3587
4006
  }
3588
4007
  function cleanupOrphanedProcesses(boardId) {
3589
4008
  const pidPath = pidFilePath(boardId);
3590
- if (existsSync2(pidPath)) {
4009
+ if (existsSync3(pidPath)) {
3591
4010
  try {
3592
- const stalePid = parseInt(readFileSync2(pidPath, "utf-8").trim(), 10);
4011
+ const stalePid = parseInt(readFileSync3(pidPath, "utf-8").trim(), 10);
3593
4012
  if (stalePid && stalePid !== process.pid) {
3594
4013
  try {
3595
4014
  process.kill(stalePid, 0);
@@ -3614,10 +4033,10 @@ function cleanupOrphanedProcesses(boardId) {
3614
4033
  console.log(`Killed ${String(manifestPids.length)} orphaned child process(es) from manifest`);
3615
4034
  removeChildManifest(boardId);
3616
4035
  }
3617
- const staleReaperPath = join2(pidDir(boardId), "reaper.pid");
4036
+ const staleReaperPath = join3(pidDir(boardId), "reaper.pid");
3618
4037
  try {
3619
- if (existsSync2(staleReaperPath)) {
3620
- const reaperPid = parseInt(readFileSync2(staleReaperPath, "utf-8").trim(), 10);
4038
+ if (existsSync3(staleReaperPath)) {
4039
+ const reaperPid = parseInt(readFileSync3(staleReaperPath, "utf-8").trim(), 10);
3621
4040
  if (reaperPid && !isNaN(reaperPid)) {
3622
4041
  try {
3623
4042
  process.kill(reaperPid, 0);
@@ -3626,7 +4045,7 @@ function cleanupOrphanedProcesses(boardId) {
3626
4045
  } catch {
3627
4046
  }
3628
4047
  }
3629
- unlinkSync2(staleReaperPath);
4048
+ unlinkSync3(staleReaperPath);
3630
4049
  }
3631
4050
  } catch {
3632
4051
  }
@@ -3673,15 +4092,15 @@ async function runPipeline(client, args) {
3673
4092
  return;
3674
4093
  }
3675
4094
  }
3676
- const gateFilePath = join2(process.cwd(), "pipeline.gates.yaml");
4095
+ const gateFilePath = join3(process.cwd(), "pipeline.gates.yaml");
3677
4096
  let gateConfig;
3678
- if (!existsSync2(gateFilePath)) {
4097
+ if (!existsSync3(gateFilePath)) {
3679
4098
  console.error(`Error: pipeline.gates.yaml not found in ${process.cwd()}`);
3680
4099
  console.error('Run "kantban pipeline init" to generate a starter gate file.');
3681
4100
  process.exit(1);
3682
4101
  }
3683
4102
  try {
3684
- const gateYaml = readFileSync2(gateFilePath, "utf-8");
4103
+ const gateYaml = readFileSync3(gateFilePath, "utf-8");
3685
4104
  gateConfig = parseGateConfig(gateYaml);
3686
4105
  console.log(`Gate config loaded: ${gateConfig.default.length} default gate(s)`);
3687
4106
  } catch (err) {
@@ -3709,7 +4128,7 @@ async function runPipeline(client, args) {
3709
4128
  intelligence_provider: opts.provider ?? void 0
3710
4129
  };
3711
4130
  const intelligenceProvider = registry.resolveForIntelligence(boardProviderConfig);
3712
- const logBaseDir = join2(homedir(), ".kantban", "pipelines");
4131
+ const logBaseDir = join3(homedir2(), ".kantban", "pipelines");
3713
4132
  const logger = new PipelineLogger(logBaseDir, opts.boardId);
3714
4133
  logger.pruneOldLogs(opts.logRetention);
3715
4134
  let runMemory = null;
@@ -3746,7 +4165,7 @@ async function runPipeline(client, args) {
3746
4165
  effectiveConfig.model = registry.resolveModel(columnProvider, effectiveConfig.model);
3747
4166
  }
3748
4167
  const columnGates = resolveGatesForColumn(gateConfig, resolvedColumnName);
3749
- const gateCwd = effectiveConfig.worktreeName ? join2(process.cwd(), effectiveConfig.worktreeName) : void 0;
4168
+ const gateCwd = effectiveConfig.worktreeName ? join3(process.cwd(), effectiveConfig.worktreeName) : void 0;
3750
4169
  const effectiveMcpConfigPath = columnGates.length > 0 ? generateGateProxyMcpConfig(
3751
4170
  client.baseUrl,
3752
4171
  client.token,
@@ -4195,7 +4614,7 @@ Received ${signal}. Shutting down gracefully...`);
4195
4614
  cleanupOrphanedProcesses(opts.boardId);
4196
4615
  writePidFile(opts.boardId);
4197
4616
  logger.orchestrator(`PID file written: ${String(process.pid)}`);
4198
- const reaperPidPath = join2(pidDir(opts.boardId), "reaper.pid");
4617
+ const reaperPidPath = join3(pidDir(opts.boardId), "reaper.pid");
4199
4618
  const reaperProcess = spawnReaper({
4200
4619
  orchestratorPid: process.pid,
4201
4620
  manifestPath: childManifestPath(opts.boardId),
@@ -4358,7 +4777,7 @@ async function stopPipeline(args) {
4358
4777
  const pidFile = pidFilePath(boardId);
4359
4778
  let pid;
4360
4779
  try {
4361
- const content = readFileSync2(pidFile, "utf8").trim();
4780
+ const content = readFileSync3(pidFile, "utf8").trim();
4362
4781
  pid = Number(content);
4363
4782
  if (isNaN(pid) || pid <= 0) {
4364
4783
  throw new Error("Invalid PID");
@@ -4389,4 +4808,4 @@ export {
4389
4808
  runPipeline,
4390
4809
  stopPipeline
4391
4810
  };
4392
- //# sourceMappingURL=pipeline-AM742SOH.js.map
4811
+ //# sourceMappingURL=pipeline-IYKEQZWN.js.map