branch-nexus 1.0.0

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/cli.js ADDED
@@ -0,0 +1,3651 @@
1
+ #!/usr/bin/env node
2
+ import * as path2 from 'path';
3
+ import { join, basename, resolve, dirname } from 'path';
4
+ import 'url';
5
+ import { readFileSync, existsSync, readdirSync, mkdirSync, appendFileSync } from 'fs';
6
+ import * as os from 'os';
7
+ import { homedir } from 'os';
8
+ import simpleGit2 from 'simple-git';
9
+ import { Command } from 'commander';
10
+ import chalk9 from 'chalk';
11
+ import * as p3 from '@clack/prompts';
12
+ import { z } from 'zod';
13
+ import Conf from 'conf';
14
+ import { execa } from 'execa';
15
+ import inquirer2 from 'inquirer';
16
+ import * as readline from 'readline';
17
+
18
+ var __defProp = Object.defineProperty;
19
+ var __getOwnPropNames = Object.getOwnPropertyNames;
20
+ var __esm = (fn, res) => function __init() {
21
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
22
+ };
23
+ var __export = (target, all) => {
24
+ for (var name in all)
25
+ __defProp(target, name, { get: all[name], enumerable: true });
26
+ };
27
+ var init_esm_shims = __esm({
28
+ "node_modules/tsup/assets/esm_shims.js"() {
29
+ }
30
+ });
31
+
32
+ // ts-src/types/errors.ts
33
+ function userFacingError(message, hint) {
34
+ if (hint !== void 0 && hint !== "") {
35
+ return `Error: ${message}. Next step: ${hint}`;
36
+ }
37
+ return `Error: ${message}.`;
38
+ }
39
+ function isBranchNexusError(error) {
40
+ return error instanceof BranchNexusError;
41
+ }
42
+ var BranchNexusError;
43
+ var init_errors = __esm({
44
+ "ts-src/types/errors.ts"() {
45
+ init_esm_shims();
46
+ BranchNexusError = class extends Error {
47
+ code;
48
+ hint;
49
+ constructor(message, code = 4 /* RUNTIME_ERROR */, hint = "") {
50
+ super(message);
51
+ this.name = "BranchNexusError";
52
+ this.code = code;
53
+ this.hint = hint;
54
+ }
55
+ toString() {
56
+ if (this.hint !== "") {
57
+ return `${this.message} Hint: ${this.hint}`;
58
+ }
59
+ return this.message;
60
+ }
61
+ };
62
+ }
63
+ });
64
+ function defaultLogPath() {
65
+ const logDir = resolve(homedir(), DEFAULT_LOG_DIR);
66
+ return resolve(logDir, DEFAULT_LOG_FILE);
67
+ }
68
+ function configureLogging(options) {
69
+ if (options?.level !== void 0) {
70
+ currentLevel = options.level;
71
+ }
72
+ if (options?.logFile !== void 0 && options.logFile !== "") {
73
+ logFilePath = resolve(options.logFile);
74
+ const logDir = dirname(logFilePath);
75
+ if (!existsSync(logDir)) {
76
+ mkdirSync(logDir, { recursive: true });
77
+ }
78
+ }
79
+ }
80
+ function formatMessage(level, message, args) {
81
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
82
+ const formattedArgs = args.length > 0 ? " " + args.map((arg) => JSON.stringify(arg)).join(" ") : "";
83
+ return `${timestamp} ${level.toUpperCase()} ${message}${formattedArgs}`;
84
+ }
85
+ function log(level, message, args) {
86
+ if (LOG_LEVELS[level] < LOG_LEVELS[currentLevel]) {
87
+ return;
88
+ }
89
+ const formatted = formatMessage(level, message, args);
90
+ if (level === "error") {
91
+ console.error(formatted);
92
+ } else if (level === "warn") {
93
+ console.warn(formatted);
94
+ } else {
95
+ console.log(formatted);
96
+ }
97
+ if (logFilePath !== null) {
98
+ try {
99
+ appendFileSync(logFilePath, formatted + "\n", "utf-8");
100
+ } catch {
101
+ }
102
+ }
103
+ }
104
+ var LOG_LEVELS, currentLevel, logFilePath, DEFAULT_LOG_DIR, DEFAULT_LOG_FILE, logger;
105
+ var init_logger = __esm({
106
+ "ts-src/utils/logger.ts"() {
107
+ init_esm_shims();
108
+ LOG_LEVELS = {
109
+ debug: 0,
110
+ info: 1,
111
+ warn: 2,
112
+ error: 3
113
+ };
114
+ currentLevel = "info";
115
+ logFilePath = null;
116
+ DEFAULT_LOG_DIR = ".config/branchnexus/logs";
117
+ DEFAULT_LOG_FILE = "branchnexus.log";
118
+ logger = {
119
+ debug: (message, ...args) => {
120
+ log("debug", message, args);
121
+ },
122
+ info: (message, ...args) => {
123
+ log("info", message, args);
124
+ },
125
+ warn: (message, ...args) => {
126
+ log("warn", message, args);
127
+ },
128
+ error: (message, ...args) => {
129
+ log("error", message, args);
130
+ }
131
+ };
132
+ }
133
+ });
134
+
135
+ // ts-src/git/branch.ts
136
+ var branch_exports = {};
137
+ __export(branch_exports, {
138
+ branchExists: () => branchExists,
139
+ getCurrentBranch: () => getCurrentBranch,
140
+ listLocalBranches: () => listLocalBranches,
141
+ remoteBranchExists: () => remoteBranchExists
142
+ });
143
+ async function listLocalBranches(repoPath) {
144
+ logger.debug(`Listing local branches for ${repoPath}`);
145
+ const git = simpleGit2(repoPath);
146
+ try {
147
+ const isRepo = await git.checkIsRepo();
148
+ if (!isRepo) {
149
+ throw new BranchNexusError(
150
+ `Not a git repository: ${repoPath}`,
151
+ 5 /* GIT_ERROR */,
152
+ "Check the path and ensure it is a valid Git repository."
153
+ );
154
+ }
155
+ } catch (error) {
156
+ if (error instanceof BranchNexusError) {
157
+ throw error;
158
+ }
159
+ throw new BranchNexusError(
160
+ `Repository is not accessible: ${repoPath}`,
161
+ 5 /* GIT_ERROR */,
162
+ "Check the path and ensure it is a valid Git repository."
163
+ );
164
+ }
165
+ let warning;
166
+ try {
167
+ const status = await git.status();
168
+ if (status.detached) {
169
+ warning = "Detached HEAD detected. Branch operations may be limited.";
170
+ logger.warn(`Detached HEAD detected repo=${repoPath}`);
171
+ }
172
+ } catch {
173
+ }
174
+ try {
175
+ const localBranches = await git.branchLocal();
176
+ const FORK_BRANCH_RE = /-pane-\d+$/;
177
+ let branchNames = localBranches.all.filter((b) => !FORK_BRANCH_RE.test(b)).sort();
178
+ try {
179
+ await git.fetch(["--all"]);
180
+ const remoteBranches = await git.branch(["-r"]);
181
+ const remoteNames = remoteBranches.all.filter((b) => !b.includes("HEAD") && !FORK_BRANCH_RE.test(b)).sort();
182
+ branchNames = [...branchNames, ...remoteNames];
183
+ logger.debug(
184
+ `Discovered ${localBranches.all.length} local + ${remoteNames.length} remote branches`
185
+ );
186
+ } catch (fetchError) {
187
+ logger.debug("Could not fetch remote branches");
188
+ }
189
+ if (branchNames.length === 0) {
190
+ throw new BranchNexusError(
191
+ "No branches found.",
192
+ 5 /* GIT_ERROR */,
193
+ "Create an initial commit and at least one branch."
194
+ );
195
+ }
196
+ logger.debug(`Total ${branchNames.length} branches available`);
197
+ return {
198
+ branches: branchNames,
199
+ warning
200
+ };
201
+ } catch (error) {
202
+ if (error instanceof BranchNexusError) {
203
+ throw error;
204
+ }
205
+ throw new BranchNexusError(
206
+ `Failed to list branches for ${repoPath}`,
207
+ 5 /* GIT_ERROR */,
208
+ "Run `git branch -a` manually to inspect repository state."
209
+ );
210
+ }
211
+ }
212
+ async function getCurrentBranch(repoPath) {
213
+ const git = simpleGit2(repoPath);
214
+ const status = await git.status();
215
+ return status.current ?? "HEAD";
216
+ }
217
+ async function branchExists(repoPath, branch) {
218
+ const git = simpleGit2(repoPath);
219
+ const branches = await git.branchLocal();
220
+ return branches.all.includes(branch);
221
+ }
222
+ async function remoteBranchExists(repoPath, remoteBranch) {
223
+ const git = simpleGit2(repoPath);
224
+ const branches = await git.branch(["-r"]);
225
+ return branches.all.includes(remoteBranch);
226
+ }
227
+ var init_branch = __esm({
228
+ "ts-src/git/branch.ts"() {
229
+ init_esm_shims();
230
+ init_logger();
231
+ init_errors();
232
+ }
233
+ });
234
+
235
+ // ts-src/cli.ts
236
+ init_esm_shims();
237
+
238
+ // ts-src/commands/index.ts
239
+ init_esm_shims();
240
+
241
+ // ts-src/commands/run.ts
242
+ init_esm_shims();
243
+ init_errors();
244
+
245
+ // ts-src/core/config.ts
246
+ init_esm_shims();
247
+
248
+ // ts-src/types/index.ts
249
+ init_esm_shims();
250
+
251
+ // ts-src/types/config.ts
252
+ init_esm_shims();
253
+ var LayoutSchema = z.enum(["horizontal", "vertical", "grid"]);
254
+ var CleanupPolicySchema = z.enum(["session", "persistent"]);
255
+ var ColorThemeSchema = z.enum(["cyan", "green", "magenta", "blue", "yellow", "red"]);
256
+ var TerminalRuntimeSchema = z.enum(["wsl", "powershell", "native"]);
257
+ var RepositoryCacheEntrySchema = z.object({
258
+ full_name: z.string(),
259
+ clone_url: z.string()
260
+ });
261
+ var PresetConfigSchema = z.object({
262
+ layout: LayoutSchema,
263
+ panes: z.number().int().min(2).max(6),
264
+ cleanup: CleanupPolicySchema
265
+ });
266
+ var AppConfigSchema = z.object({
267
+ defaultRoot: z.string().default(""),
268
+ remoteRepoUrl: z.string().default(""),
269
+ githubToken: z.string().default(""),
270
+ githubRepositoriesCache: z.array(RepositoryCacheEntrySchema).default([]),
271
+ githubBranchesCache: z.record(z.array(z.string())).default({}),
272
+ defaultLayout: LayoutSchema.default("grid"),
273
+ defaultPanes: z.number().int().min(2).max(6).default(4),
274
+ cleanupPolicy: CleanupPolicySchema.default("session"),
275
+ tmuxAutoInstall: z.boolean().default(true),
276
+ wslDistribution: z.string().default(""),
277
+ terminalDefaultRuntime: TerminalRuntimeSchema.default("wsl"),
278
+ terminalMaxCount: z.number().int().min(2).max(16).default(16),
279
+ sessionRestoreEnabled: z.boolean().default(true),
280
+ lastSession: z.record(z.unknown()).default({}),
281
+ colorTheme: ColorThemeSchema.default("cyan"),
282
+ presets: z.record(PresetConfigSchema).default({}),
283
+ commandHooks: z.record(z.array(z.string())).default({})
284
+ });
285
+ var DEFAULT_CONFIG = {
286
+ defaultRoot: "",
287
+ remoteRepoUrl: "",
288
+ githubToken: "",
289
+ githubRepositoriesCache: [],
290
+ githubBranchesCache: {},
291
+ defaultLayout: "grid",
292
+ defaultPanes: 4,
293
+ cleanupPolicy: "session",
294
+ tmuxAutoInstall: true,
295
+ wslDistribution: "",
296
+ terminalDefaultRuntime: "wsl",
297
+ terminalMaxCount: 16,
298
+ sessionRestoreEnabled: true,
299
+ lastSession: {},
300
+ colorTheme: "cyan",
301
+ presets: {},
302
+ commandHooks: {}
303
+ };
304
+
305
+ // ts-src/types/index.ts
306
+ init_errors();
307
+
308
+ // ts-src/types/worktree.ts
309
+ init_esm_shims();
310
+ function createWorktreeAssignment(pane, repoPath, branch) {
311
+ return { pane, repoPath, branch };
312
+ }
313
+ function createManagedWorktree(assignment, path3) {
314
+ return {
315
+ pane: assignment.pane,
316
+ repoPath: assignment.repoPath,
317
+ branch: assignment.branch,
318
+ path: path3
319
+ };
320
+ }
321
+
322
+ // ts-src/types/session.ts
323
+ init_esm_shims();
324
+ function isSessionSnapshot(value) {
325
+ if (typeof value !== "object" || value === null) {
326
+ return false;
327
+ }
328
+ const snapshot = value;
329
+ return typeof snapshot["layout"] === "string" && typeof snapshot["templateCount"] === "number" && Array.isArray(snapshot["terminals"]);
330
+ }
331
+
332
+ // ts-src/core/config.ts
333
+ init_errors();
334
+ var GITHUB_TOKEN_ENV = "BRANCHNEXUS_GH_TOKEN";
335
+ var configStore = new Conf({
336
+ projectName: "branchnexus",
337
+ configName: "config",
338
+ defaults: DEFAULT_CONFIG,
339
+ schema: {
340
+ defaultRoot: { type: "string" },
341
+ remoteRepoUrl: { type: "string" },
342
+ githubToken: { type: "string" },
343
+ githubRepositoriesCache: { type: "array" },
344
+ githubBranchesCache: { type: "object" },
345
+ defaultLayout: { type: "string", enum: ["horizontal", "vertical", "grid"] },
346
+ defaultPanes: { type: "number", minimum: 2, maximum: 6 },
347
+ cleanupPolicy: { type: "string", enum: ["session", "persistent"] },
348
+ tmuxAutoInstall: { type: "boolean" },
349
+ wslDistribution: { type: "string" },
350
+ terminalDefaultRuntime: { type: "string", enum: ["wsl", "powershell", "native"] },
351
+ terminalMaxCount: { type: "number", minimum: 2, maximum: 16 },
352
+ sessionRestoreEnabled: { type: "boolean" },
353
+ lastSession: { type: "object" },
354
+ colorTheme: { type: "string", enum: ["cyan", "green", "magenta", "blue", "yellow", "red"] },
355
+ presets: { type: "object" },
356
+ commandHooks: { type: "object" }
357
+ }
358
+ });
359
+ function getConfigPath() {
360
+ return configStore.path;
361
+ }
362
+ function loadConfig() {
363
+ try {
364
+ const raw = configStore.store;
365
+ const config = AppConfigSchema.parse(raw);
366
+ const envToken = process.env[GITHUB_TOKEN_ENV];
367
+ if (envToken !== void 0 && envToken.trim() !== "") {
368
+ config.githubToken = envToken.trim();
369
+ }
370
+ return config;
371
+ } catch (error) {
372
+ if (error instanceof z.ZodError) {
373
+ throw new BranchNexusError(
374
+ "Configuration validation failed",
375
+ 3 /* CONFIG_ERROR */,
376
+ error.message
377
+ );
378
+ }
379
+ return { ...DEFAULT_CONFIG };
380
+ }
381
+ }
382
+ function saveConfig(config) {
383
+ const validated = AppConfigSchema.parse(config);
384
+ configStore.store = validated;
385
+ }
386
+ function resetConfig() {
387
+ configStore.store = DEFAULT_CONFIG;
388
+ return { ...DEFAULT_CONFIG };
389
+ }
390
+ function setWslDistribution(distribution) {
391
+ const config = loadConfig();
392
+ config.wslDistribution = distribution;
393
+ saveConfig(config);
394
+ return config;
395
+ }
396
+ function setGithubToken(token) {
397
+ const config = loadConfig();
398
+ config.githubToken = token;
399
+ saveConfig(config);
400
+ return config;
401
+ }
402
+ function updateGithubRepoCache(repos) {
403
+ const config = loadConfig();
404
+ config.githubRepositoriesCache = repos.map((repo) => ({
405
+ full_name: repo.full_name,
406
+ clone_url: repo.clone_url
407
+ }));
408
+ saveConfig(config);
409
+ return config;
410
+ }
411
+ function updateLastSession(session) {
412
+ const config = loadConfig();
413
+ config.lastSession = session;
414
+ saveConfig(config);
415
+ return config;
416
+ }
417
+ function setConfigValue(key, value) {
418
+ const config = loadConfig();
419
+ switch (key) {
420
+ case "defaultRoot":
421
+ config.defaultRoot = value;
422
+ break;
423
+ case "remoteRepoUrl":
424
+ config.remoteRepoUrl = value;
425
+ break;
426
+ case "githubToken":
427
+ config.githubToken = value;
428
+ break;
429
+ case "defaultLayout":
430
+ if (["horizontal", "vertical", "grid"].includes(value)) {
431
+ config.defaultLayout = value;
432
+ }
433
+ break;
434
+ case "defaultPanes":
435
+ config.defaultPanes = parseInt(value, 10);
436
+ break;
437
+ case "cleanupPolicy":
438
+ if (["session", "persistent"].includes(value)) {
439
+ config.cleanupPolicy = value;
440
+ }
441
+ break;
442
+ case "wslDistribution":
443
+ config.wslDistribution = value;
444
+ break;
445
+ case "terminalMaxCount":
446
+ config.terminalMaxCount = parseInt(value, 10);
447
+ break;
448
+ case "tmuxAutoInstall":
449
+ config.tmuxAutoInstall = value === "true";
450
+ break;
451
+ case "sessionRestoreEnabled":
452
+ config.sessionRestoreEnabled = value === "true";
453
+ break;
454
+ case "colorTheme":
455
+ if (["cyan", "green", "magenta", "blue", "yellow", "red"].includes(value)) {
456
+ config.colorTheme = value;
457
+ }
458
+ break;
459
+ default:
460
+ throw new BranchNexusError(
461
+ `Unknown config key: ${key}`,
462
+ 3 /* CONFIG_ERROR */,
463
+ 'Use "branchnexus config show" to see available keys.'
464
+ );
465
+ }
466
+ saveConfig(config);
467
+ return config;
468
+ }
469
+
470
+ // ts-src/core/orchestrator.ts
471
+ init_esm_shims();
472
+ init_errors();
473
+
474
+ // ts-src/runtime/wsl.ts
475
+ init_esm_shims();
476
+ init_logger();
477
+ init_errors();
478
+
479
+ // ts-src/runtime/platform.ts
480
+ init_esm_shims();
481
+ function detectPlatform() {
482
+ const platform2 = os.platform();
483
+ if (platform2 === "win32") {
484
+ return "windows" /* WINDOWS */;
485
+ }
486
+ if (platform2 === "darwin") {
487
+ return "macos" /* MACOS */;
488
+ }
489
+ return "linux" /* LINUX */;
490
+ }
491
+ async function hasTmux() {
492
+ try {
493
+ await execa("tmux", ["-V"]);
494
+ return true;
495
+ } catch {
496
+ return false;
497
+ }
498
+ }
499
+ function expandHomeDir(filepath) {
500
+ if (filepath.startsWith("~/")) {
501
+ return path2.join(os.homedir(), filepath.slice(2));
502
+ }
503
+ return filepath;
504
+ }
505
+
506
+ // ts-src/runtime/wsl.ts
507
+ async function listDistributions() {
508
+ logger.debug("Listing WSL distributions using wsl.exe -l -q");
509
+ if (detectPlatform() !== "windows" /* WINDOWS */) {
510
+ throw new BranchNexusError(
511
+ "WSL is only available on Windows",
512
+ 8 /* UNSUPPORTED_PLATFORM */,
513
+ "Use native tmux on this platform."
514
+ );
515
+ }
516
+ try {
517
+ const result = await execa("wsl.exe", ["-l", "-q"], {
518
+ encoding: "buffer"
519
+ });
520
+ const output = decodeWslOutput(result.stdout);
521
+ const distros = output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).sort();
522
+ if (distros.length === 0) {
523
+ throw new BranchNexusError(
524
+ "No WSL distributions were found.",
525
+ 4 /* RUNTIME_ERROR */,
526
+ "Install a distribution using `wsl --install` and retry."
527
+ );
528
+ }
529
+ logger.debug(`Discovered ${distros.length} WSL distributions`);
530
+ return distros;
531
+ } catch (error) {
532
+ if (error instanceof BranchNexusError) {
533
+ throw error;
534
+ }
535
+ const message = error instanceof Error ? error.message : String(error);
536
+ throw new BranchNexusError(
537
+ "Failed to list WSL distributions.",
538
+ 4 /* RUNTIME_ERROR */,
539
+ message
540
+ );
541
+ }
542
+ }
543
+ function decodeWslOutput(buffer) {
544
+ if (buffer.includes(0)) {
545
+ try {
546
+ return buffer.toString("utf-16le").replace(/\ufeff/g, "");
547
+ } catch {
548
+ }
549
+ }
550
+ return buffer.toString("utf-8");
551
+ }
552
+ function validateDistribution(distribution, available) {
553
+ return available.includes(distribution);
554
+ }
555
+ function buildWslCommand(distribution, command) {
556
+ if (distribution === "") {
557
+ throw new BranchNexusError(
558
+ "WSL distribution is required.",
559
+ 7 /* VALIDATION_ERROR */,
560
+ "Select a distribution before orchestration."
561
+ );
562
+ }
563
+ if (command.length === 0) {
564
+ throw new BranchNexusError(
565
+ "Runtime command is empty.",
566
+ 7 /* VALIDATION_ERROR */,
567
+ "Provide a command to execute in WSL."
568
+ );
569
+ }
570
+ return ["wsl.exe", "-d", distribution, "--", ...command];
571
+ }
572
+
573
+ // ts-src/tmux/layouts.ts
574
+ init_esm_shims();
575
+ init_errors();
576
+ var VALID_LAYOUTS = /* @__PURE__ */ new Set(["horizontal", "vertical", "grid"]);
577
+ function validateLayout(layout, panes) {
578
+ if (!VALID_LAYOUTS.has(layout)) {
579
+ throw new BranchNexusError(
580
+ `Unsupported layout: ${layout}`,
581
+ 7 /* VALIDATION_ERROR */,
582
+ "Use horizontal, vertical, or grid."
583
+ );
584
+ }
585
+ if (panes < 1 || panes > 6) {
586
+ throw new BranchNexusError(
587
+ `Invalid pane count: ${panes}`,
588
+ 7 /* VALIDATION_ERROR */,
589
+ "Use a pane value between 1 and 6."
590
+ );
591
+ }
592
+ }
593
+ function buildLayoutCommands(sessionName, layout, panePaths, colorTheme, paneBranches, paneNames, startupCommands) {
594
+ const panes = panePaths.length;
595
+ validateLayout(layout, panes);
596
+ const tmuxColor = colorTheme ?? "cyan";
597
+ const commands = [
598
+ ["tmux", "new-session", "-d", "-s", sessionName, "-c", panePaths[0]],
599
+ ["tmux", "set-option", "-t", sessionName, "mouse", "on"],
600
+ ["tmux", "bind-key", "-n", "WheelUpPane", "send-keys", "-M"],
601
+ ["tmux", "bind-key", "-n", "WheelDownPane", "send-keys", "-M"],
602
+ // Apply color theme
603
+ ["tmux", "set-option", "-t", sessionName, "status-style", `bg=${tmuxColor},fg=black`],
604
+ ["tmux", "set-option", "-t", sessionName, "pane-border-style", `fg=${tmuxColor}`],
605
+ ["tmux", "set-option", "-t", sessionName, "pane-active-border-style", `fg=${tmuxColor},bold`],
606
+ ["tmux", "set-option", "-t", sessionName, "status-left", ` ${sessionName} `],
607
+ ["tmux", "set-option", "-t", sessionName, "status-left-style", `bg=${tmuxColor},fg=black,bold`],
608
+ ["tmux", "set-option", "-t", sessionName, "status-right", " %H:%M %d-%b-%y "],
609
+ ["tmux", "set-option", "-t", sessionName, "status-right-style", `bg=${tmuxColor},fg=black`],
610
+ ["tmux", "set-option", "-t", sessionName, "window-status-format", ""],
611
+ ["tmux", "set-option", "-t", sessionName, "window-status-current-format", ""],
612
+ // Pane border labels with blinking indicator for active pane
613
+ ["tmux", "set-option", "-t", sessionName, "pane-border-status", "top"],
614
+ [
615
+ "tmux",
616
+ "set-option",
617
+ "-t",
618
+ sessionName,
619
+ "pane-border-format",
620
+ `#{?pane_active,#[fg=${tmuxColor}#,blink] \u25CF #[noblink#,bold]#{pane_title} #[default],#[fg=grey] \u25CB #{pane_title} #[default]}`
621
+ ]
622
+ ];
623
+ function paneTitle(index) {
624
+ const customName = paneNames?.[index] ?? "";
625
+ const branch = paneBranches?.[index]?.replace(/^origin\//, "") ?? "";
626
+ if (customName !== "") return `${customName} [${branch}]`;
627
+ return `Pane ${index} [${branch}]`;
628
+ }
629
+ commands.push([
630
+ "tmux",
631
+ "select-pane",
632
+ "-t",
633
+ `${sessionName}:0.0`,
634
+ "-T",
635
+ paneTitle(0)
636
+ ]);
637
+ for (let index = 1; index < panes; index++) {
638
+ let splitFlag;
639
+ if (layout === "horizontal") {
640
+ splitFlag = "-h";
641
+ } else if (layout === "vertical") {
642
+ splitFlag = "-v";
643
+ } else {
644
+ splitFlag = index % 2 === 0 ? "-v" : "-h";
645
+ }
646
+ commands.push([
647
+ "tmux",
648
+ "split-window",
649
+ splitFlag,
650
+ "-t",
651
+ `${sessionName}:0`,
652
+ "-c",
653
+ panePaths[index]
654
+ ]);
655
+ commands.push([
656
+ "tmux",
657
+ "select-pane",
658
+ "-t",
659
+ `${sessionName}:0.${index}`,
660
+ "-T",
661
+ paneTitle(index)
662
+ ]);
663
+ }
664
+ let tmuxLayout;
665
+ if (layout === "horizontal") {
666
+ tmuxLayout = "even-horizontal";
667
+ } else if (layout === "vertical") {
668
+ tmuxLayout = "even-vertical";
669
+ } else {
670
+ tmuxLayout = "tiled";
671
+ }
672
+ commands.push(["tmux", "select-layout", "-t", `${sessionName}:0`, tmuxLayout]);
673
+ commands.push([
674
+ "tmux",
675
+ "set-hook",
676
+ "-t",
677
+ sessionName,
678
+ "client-resized",
679
+ `select-layout -t ${sessionName}:0 ${tmuxLayout}`
680
+ ]);
681
+ commands.push(["tmux", "select-pane", "-t", `${sessionName}:0.0`]);
682
+ if (startupCommands !== void 0 && startupCommands.length > 0) {
683
+ for (let i = 0; i < panes; i++) {
684
+ const cmd = startupCommands[i];
685
+ if (cmd !== void 0 && cmd.trim() !== "") {
686
+ commands.push([
687
+ "tmux",
688
+ "send-keys",
689
+ "-t",
690
+ `${sessionName}:0.${i}`,
691
+ cmd.trim(),
692
+ "Enter"
693
+ ]);
694
+ }
695
+ }
696
+ }
697
+ return commands;
698
+ }
699
+
700
+ // ts-src/tmux/session.ts
701
+ init_esm_shims();
702
+
703
+ // ts-src/runtime/shell.ts
704
+ init_esm_shims();
705
+ init_logger();
706
+ init_errors();
707
+ async function runCommand(command, options = {}) {
708
+ const [cmd, ...args] = command;
709
+ logger.debug(`Running command: ${command.join(" ")}`);
710
+ try {
711
+ const result = await execa(cmd, args, {
712
+ cwd: options.cwd,
713
+ timeout: options.timeout ?? 3e4,
714
+ input: options.input,
715
+ reject: false,
716
+ all: true
717
+ });
718
+ return {
719
+ exitCode: result.exitCode,
720
+ stdout: result.stdout,
721
+ stderr: result.stderr
722
+ };
723
+ } catch (error) {
724
+ const execaError = error;
725
+ return {
726
+ exitCode: execaError.exitCode ?? 1,
727
+ stdout: execaError.stdout ?? "",
728
+ stderr: execaError.stderr ?? execaError.message
729
+ };
730
+ }
731
+ }
732
+ async function runCommandViaWSL(distribution, command, options = {}) {
733
+ const wrapped = buildWslCommand(distribution, command);
734
+ logger.debug(`Running WSL command: ${wrapped.join(" ")}`);
735
+ return runCommand(wrapped, options);
736
+ }
737
+
738
+ // ts-src/tmux/session.ts
739
+ init_logger();
740
+ init_errors();
741
+ async function startSession(sessionName, commands, distribution) {
742
+ const isWindows = detectPlatform() === "windows" /* WINDOWS */;
743
+ logger.debug(`Starting tmux session: ${sessionName}`);
744
+ for (const command of commands) {
745
+ const wrappedCommand = isWindows && distribution !== void 0 && distribution !== "" ? buildWslCommand(distribution, command) : command;
746
+ logger.debug(`Executing tmux command: ${wrappedCommand.join(" ")}`);
747
+ const result = isWindows && distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, command) : await runCommand(wrappedCommand);
748
+ if (result.exitCode !== 0) {
749
+ const stderr = result.stderr.trim();
750
+ const isNewSession = command[0] === "tmux" && command[1] === "new-session";
751
+ if (isNewSession && stderr.toLowerCase().includes("duplicate session")) {
752
+ logger.warn(`tmux session already exists: ${sessionName}, replacing`);
753
+ await killSession(sessionName, distribution);
754
+ const retryResult = isWindows && distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, command) : await runCommand(wrappedCommand);
755
+ if (retryResult.exitCode !== 0) {
756
+ throw new BranchNexusError(
757
+ "Failed to initialize tmux session.",
758
+ 6 /* TMUX_ERROR */,
759
+ retryResult.stderr || "tmux command failed"
760
+ );
761
+ }
762
+ continue;
763
+ }
764
+ throw new BranchNexusError(
765
+ "Failed to initialize tmux session.",
766
+ 6 /* TMUX_ERROR */,
767
+ stderr || "tmux command failed"
768
+ );
769
+ }
770
+ }
771
+ logger.info(`tmux session ready: ${sessionName}`);
772
+ }
773
+ async function killSession(sessionName, distribution) {
774
+ const isWindows = detectPlatform() === "windows" /* WINDOWS */;
775
+ const command = ["tmux", "kill-session", "-t", sessionName];
776
+ logger.debug(`Killing tmux session: ${sessionName}`);
777
+ const result = isWindows && distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, command) : await runCommand(command);
778
+ if (result.exitCode !== 0) {
779
+ logger.warn(`Failed to kill session: ${result.stderr}`);
780
+ }
781
+ }
782
+ async function listSessions(distribution) {
783
+ const isWindows = detectPlatform() === "windows" /* WINDOWS */;
784
+ const command = ["tmux", "list-sessions", "-F", "#{session_name}"];
785
+ try {
786
+ const result = isWindows && distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, command) : await runCommand(command);
787
+ if (result.exitCode !== 0) {
788
+ return [];
789
+ }
790
+ return result.stdout.split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
791
+ } catch {
792
+ return [];
793
+ }
794
+ }
795
+
796
+ // ts-src/git/worktree.ts
797
+ init_esm_shims();
798
+ init_logger();
799
+ init_errors();
800
+ var SANITIZE_PATTERN = /[^A-Za-z0-9._-]+/g;
801
+ var WorktreeManager = class {
802
+ baseDir;
803
+ cleanupPolicy;
804
+ posixMode;
805
+ managed = [];
806
+ constructor(baseDir, cleanupPolicy = "session") {
807
+ this.baseDir = baseDir;
808
+ this.cleanupPolicy = cleanupPolicy;
809
+ this.posixMode = false;
810
+ }
811
+ setPosixMode(enabled) {
812
+ this.posixMode = enabled;
813
+ }
814
+ safe(value) {
815
+ const cleaned = value.replace(SANITIZE_PATTERN, "-").replace(/^-+|-+$/g, "");
816
+ return cleaned || "default";
817
+ }
818
+ asPosix(value) {
819
+ let normalized = value.replace(/\\/g, "/");
820
+ while (normalized.startsWith("//")) {
821
+ normalized = normalized.slice(1);
822
+ }
823
+ return normalized;
824
+ }
825
+ commandPath(value) {
826
+ return this.posixMode ? this.asPosix(value) : value;
827
+ }
828
+ buildWorktreePath(assignment) {
829
+ const repoName = this.safe(this.asPosix(assignment.repoPath).split("/").pop() ?? "repo");
830
+ const branchSlug = this.safe(assignment.branch.replace(/^bnx-/, "").replace(/^origin\//, ""));
831
+ return `${this.baseDir}/${repoName}/${branchSlug}`;
832
+ }
833
+ async addWorktree(assignment, distribution) {
834
+ const target = this.buildWorktreePath(assignment);
835
+ logger.debug(
836
+ `Adding worktree pane=${assignment.pane} repo=${assignment.repoPath} branch=${assignment.branch} target=${target}`
837
+ );
838
+ const repoPath = this.commandPath(assignment.repoPath);
839
+ const targetPath = this.commandPath(target);
840
+ const pruneCmd = ["git", "-C", repoPath, "worktree", "prune"];
841
+ try {
842
+ if (distribution !== void 0 && distribution !== "") {
843
+ await runCommandViaWSL(distribution, pruneCmd);
844
+ } else {
845
+ await runCommand(pruneCmd);
846
+ }
847
+ } catch {
848
+ }
849
+ const existingWorktrees = await this.getWorktreesForBranch(
850
+ assignment.repoPath,
851
+ assignment.branch,
852
+ distribution
853
+ );
854
+ if (existingWorktrees.length > 0) {
855
+ const existingPath = existingWorktrees[0];
856
+ if (this.isUnderBaseDir(existingPath) || existingPath === repoPath) {
857
+ logger.info(`Reusing existing worktree at ${existingPath}`);
858
+ const managed = createManagedWorktree(assignment, existingPath);
859
+ this.managed.push(managed);
860
+ return managed;
861
+ }
862
+ logger.warn(`Removing stale worktree at ${existingPath}`);
863
+ const removeCmd = ["git", "-C", repoPath, "worktree", "remove", "--force", this.commandPath(existingPath)];
864
+ try {
865
+ if (distribution !== void 0 && distribution !== "") {
866
+ await runCommandViaWSL(distribution, removeCmd);
867
+ } else {
868
+ await runCommand(removeCmd);
869
+ }
870
+ logger.debug(`Removed stale worktree: ${existingPath}`);
871
+ } catch {
872
+ const pruneCmd2 = ["git", "-C", repoPath, "worktree", "prune"];
873
+ if (distribution !== void 0 && distribution !== "") {
874
+ await runCommandViaWSL(distribution, pruneCmd2);
875
+ } else {
876
+ await runCommand(pruneCmd2);
877
+ }
878
+ logger.debug("Pruned stale worktree references");
879
+ }
880
+ }
881
+ const cmd = ["git", "-C", repoPath, "worktree", "add", targetPath, assignment.branch];
882
+ try {
883
+ if (distribution !== void 0 && distribution !== "") {
884
+ await runCommandViaWSL(distribution, cmd);
885
+ } else {
886
+ await runCommand(cmd);
887
+ }
888
+ logger.debug(`Created worktree at ${target}`);
889
+ const managed = createManagedWorktree(assignment, target);
890
+ this.managed.push(managed);
891
+ return managed;
892
+ } catch (error) {
893
+ const message = error instanceof Error ? error.message : String(error);
894
+ throw new BranchNexusError(
895
+ `Failed to create worktree for pane ${assignment.pane}`,
896
+ 5 /* GIT_ERROR */,
897
+ message
898
+ );
899
+ }
900
+ }
901
+ async getCurrentBranch(repoPath, distribution) {
902
+ const cmd = ["git", "-C", repoPath, "branch", "--show-current"];
903
+ try {
904
+ const result = distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, cmd) : await runCommand(cmd);
905
+ return result.stdout.trim();
906
+ } catch {
907
+ return "";
908
+ }
909
+ }
910
+ async materialize(assignments, distribution) {
911
+ logger.debug(`Materializing ${assignments.length} worktree assignments`);
912
+ const created = [];
913
+ for (const assignment of assignments.sort((a, b) => a.pane - b.pane)) {
914
+ created.push(await this.addWorktree(assignment, distribution));
915
+ }
916
+ return created;
917
+ }
918
+ async checkDirty(worktree, distribution) {
919
+ const cmd = ["git", "-C", this.commandPath(worktree.path), "status", "--porcelain"];
920
+ try {
921
+ const result = distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, cmd) : await runCommand(cmd);
922
+ const dirty = result.stdout.trim().length > 0;
923
+ logger.debug(`Dirty check path=${worktree.path} dirty=${dirty}`);
924
+ return dirty;
925
+ } catch (error) {
926
+ const message = error instanceof Error ? error.message : String(error);
927
+ throw new BranchNexusError(
928
+ `Dirty check failed for ${worktree.path}`,
929
+ 5 /* GIT_ERROR */,
930
+ message
931
+ );
932
+ }
933
+ }
934
+ async cleanup(options) {
935
+ if (this.cleanupPolicy === "persistent" && options?.ignorePolicy !== true) {
936
+ logger.debug("Cleanup skipped due to persistent policy");
937
+ return [];
938
+ }
939
+ const targets = options?.selected ?? this.managed;
940
+ const removed = [];
941
+ const removedPaths = /* @__PURE__ */ new Set();
942
+ for (const worktree of targets) {
943
+ const worktreePath = this.commandPath(worktree.path);
944
+ if (removedPaths.has(worktreePath)) {
945
+ continue;
946
+ }
947
+ removedPaths.add(worktreePath);
948
+ const cmd = ["git", "-C", this.commandPath(worktree.repoPath), "worktree", "remove"];
949
+ if (options?.force !== false) {
950
+ cmd.push("--force");
951
+ }
952
+ cmd.push(worktreePath);
953
+ try {
954
+ if (options?.distribution !== void 0 && options.distribution !== "") {
955
+ await runCommandViaWSL(options.distribution, cmd);
956
+ } else {
957
+ await runCommand(cmd);
958
+ }
959
+ removed.push(worktreePath);
960
+ logger.debug(`Removed worktree at ${worktreePath}`);
961
+ } catch (error) {
962
+ const message = error instanceof Error ? error.message : String(error);
963
+ throw new BranchNexusError(
964
+ `Cleanup failed for ${worktreePath}`,
965
+ 5 /* GIT_ERROR */,
966
+ message
967
+ );
968
+ }
969
+ }
970
+ return removed;
971
+ }
972
+ getManaged() {
973
+ return [...this.managed];
974
+ }
975
+ getCleanupPolicy() {
976
+ return this.cleanupPolicy;
977
+ }
978
+ isUnderBaseDir(path3) {
979
+ const normalizedBase = this.asPosix(this.baseDir);
980
+ const normalizedPath = this.asPosix(path3);
981
+ return normalizedPath.startsWith(normalizedBase);
982
+ }
983
+ async getWorktreesForBranch(repoPath, branch, distribution) {
984
+ const cmd = ["git", "-C", this.commandPath(repoPath), "worktree", "list", "--porcelain"];
985
+ try {
986
+ const result = distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, cmd) : await runCommand(cmd);
987
+ const worktrees = [];
988
+ let currentWorktree = "";
989
+ const expectedRef = branch.startsWith("refs/heads/") ? branch : `refs/heads/${branch}`;
990
+ for (const line of result.stdout.split("\n")) {
991
+ const trimmed = line.trim();
992
+ if (trimmed === "") continue;
993
+ if (trimmed.startsWith("worktree ")) {
994
+ currentWorktree = trimmed.slice(9).trim();
995
+ } else if (trimmed.startsWith("branch ") && currentWorktree !== "") {
996
+ const branchRef = trimmed.slice(7).trim();
997
+ if (branchRef === expectedRef) {
998
+ worktrees.push(currentWorktree);
999
+ }
1000
+ }
1001
+ }
1002
+ return worktrees;
1003
+ } catch {
1004
+ return [];
1005
+ }
1006
+ }
1007
+ };
1008
+
1009
+ // ts-src/git/clone.ts
1010
+ init_esm_shims();
1011
+ init_logger();
1012
+ init_errors();
1013
+
1014
+ // ts-src/utils/validators.ts
1015
+ init_esm_shims();
1016
+ function normalizePath(path3) {
1017
+ return path3.replace(/\\/g, "/");
1018
+ }
1019
+ function posixPath(path3) {
1020
+ let normalized = normalizePath(path3);
1021
+ while (normalized.startsWith("//")) {
1022
+ normalized = normalized.slice(1);
1023
+ }
1024
+ return normalized;
1025
+ }
1026
+
1027
+ // ts-src/git/clone.ts
1028
+ async function materializeRemoteBranch(repoPath, remoteBranch, _distribution) {
1029
+ const normalizedRepo = posixPath(repoPath);
1030
+ if (!remoteBranch.includes("/")) {
1031
+ throw new BranchNexusError(
1032
+ `Invalid remote branch format: ${remoteBranch}`,
1033
+ 7 /* VALIDATION_ERROR */,
1034
+ "Use branch names like origin/feature-x."
1035
+ );
1036
+ }
1037
+ const localBranch = remoteBranch.split("/").slice(1).join("/");
1038
+ logger.debug(
1039
+ `Materializing remote branch repo=${normalizedRepo} remote=${remoteBranch} local=${localBranch}`
1040
+ );
1041
+ const git = simpleGit2(normalizedRepo);
1042
+ try {
1043
+ const localBranches = await git.branchLocal();
1044
+ if (localBranches.all.includes(localBranch)) {
1045
+ logger.debug(`Local branch already exists: ${localBranch}`);
1046
+ return localBranch;
1047
+ }
1048
+ } catch {
1049
+ }
1050
+ try {
1051
+ await git.branch(["--track", localBranch, remoteBranch]);
1052
+ logger.debug(`Created tracking branch: ${localBranch}`);
1053
+ return localBranch;
1054
+ } catch (error) {
1055
+ const message = error instanceof Error ? error.message : String(error);
1056
+ throw new BranchNexusError(
1057
+ `Failed to materialize remote branch ${remoteBranch}`,
1058
+ 5 /* GIT_ERROR */,
1059
+ message
1060
+ );
1061
+ }
1062
+ }
1063
+ async function cloneRepository(url, targetPath) {
1064
+ logger.debug(`Cloning repository ${url} to ${targetPath}`);
1065
+ const git = simpleGit2();
1066
+ try {
1067
+ await git.clone(url, targetPath);
1068
+ logger.debug(`Cloned repository to ${targetPath}`);
1069
+ } catch (error) {
1070
+ const message = error instanceof Error ? error.message : String(error);
1071
+ throw new BranchNexusError(`Failed to clone repository: ${url}`, 5 /* GIT_ERROR */, message);
1072
+ }
1073
+ }
1074
+
1075
+ // ts-src/core/orchestrator.ts
1076
+ init_logger();
1077
+ async function orchestrate(request) {
1078
+ const platform2 = detectPlatform();
1079
+ const isWindows = platform2 === "windows" /* WINDOWS */;
1080
+ const sessionName = request.sessionName !== void 0 && request.sessionName !== "" ? request.sessionName : "branchnexus";
1081
+ logger.debug(
1082
+ `Starting orchestration distribution=${request.distribution} layout=${request.layout} panes=${request.assignments.length}`
1083
+ );
1084
+ if (isWindows && request.distribution !== "") {
1085
+ if (!validateDistribution(request.distribution, request.availableDistributions)) {
1086
+ logger.error(`Invalid WSL distribution: ${request.distribution}`);
1087
+ throw new BranchNexusError(
1088
+ `Invalid WSL distribution: ${request.distribution}`,
1089
+ 4 /* RUNTIME_ERROR */,
1090
+ "Re-open WSL selection and choose a discovered distribution."
1091
+ );
1092
+ }
1093
+ }
1094
+ logger.debug("Proceeding with orchestration");
1095
+ const worktreeManager = new WorktreeManager(request.worktreeBase, request.cleanupPolicy);
1096
+ if (isWindows) {
1097
+ worktreeManager.setPosixMode(true);
1098
+ }
1099
+ const executedCommands = [];
1100
+ const normalizedAssignments = [];
1101
+ logger.debug("Preparing selected branches");
1102
+ for (const assignment of request.assignments.sort((a, b) => a.pane - b.pane)) {
1103
+ let localBranch = assignment.branch;
1104
+ if (assignment.branch.startsWith("origin/")) {
1105
+ logger.debug(
1106
+ `Materializing remote branch pane=${assignment.pane} repo=${assignment.repoPath} branch=${assignment.branch}`
1107
+ );
1108
+ localBranch = await materializeRemoteBranch(assignment.repoPath, assignment.branch);
1109
+ }
1110
+ normalizedAssignments.push(
1111
+ createWorktreeAssignment(assignment.pane, assignment.repoPath, localBranch)
1112
+ );
1113
+ }
1114
+ logger.debug(`Prepared ${normalizedAssignments.length} pane assignments`);
1115
+ let worktrees = [];
1116
+ try {
1117
+ logger.debug("Creating worktrees");
1118
+ worktrees = await worktreeManager.materialize(
1119
+ normalizedAssignments,
1120
+ isWindows ? request.distribution : void 0
1121
+ );
1122
+ logger.debug(`Created ${worktrees.length} worktrees`);
1123
+ const sorted = worktrees.sort((a, b) => a.pane - b.pane);
1124
+ const panePaths = sorted.map((w) => w.path);
1125
+ const paneBranches = request.displayBranches ?? sorted.map((w) => w.branch);
1126
+ const tmuxCommands = buildLayoutCommands(
1127
+ sessionName,
1128
+ request.layout,
1129
+ panePaths,
1130
+ request.colorTheme,
1131
+ paneBranches,
1132
+ request.paneNames,
1133
+ request.startupCommands
1134
+ );
1135
+ logger.debug("Starting tmux session");
1136
+ await startSession(sessionName, tmuxCommands, isWindows ? request.distribution : void 0);
1137
+ logger.debug("Orchestration finished successfully");
1138
+ return {
1139
+ worktrees,
1140
+ executedCommands
1141
+ };
1142
+ } catch (error) {
1143
+ if (worktrees.length > 0) {
1144
+ try {
1145
+ const removed = await worktreeManager.cleanup({
1146
+ ignorePolicy: true,
1147
+ selected: worktrees,
1148
+ distribution: isWindows ? request.distribution : void 0
1149
+ });
1150
+ logger.warn(`Orchestration rollback removed ${removed.length} worktrees`);
1151
+ } catch (cleanupError) {
1152
+ logger.error(`Orchestration rollback cleanup failed: ${String(cleanupError)}`);
1153
+ }
1154
+ }
1155
+ throw error;
1156
+ }
1157
+ }
1158
+
1159
+ // ts-src/prompts/wsl.ts
1160
+ init_esm_shims();
1161
+ init_errors();
1162
+ init_logger();
1163
+ async function promptWslDistribution() {
1164
+ const platform2 = detectPlatform();
1165
+ if (platform2 !== "windows" /* WINDOWS */) {
1166
+ throw new BranchNexusError(
1167
+ "WSL is only available on Windows",
1168
+ 8 /* UNSUPPORTED_PLATFORM */,
1169
+ "Use native tmux on this platform."
1170
+ );
1171
+ }
1172
+ const config = loadConfig();
1173
+ const cachedDistribution = config.wslDistribution;
1174
+ logger.debug("Prompting for WSL distribution");
1175
+ const distributions = await listDistributions();
1176
+ if (cachedDistribution && distributions.includes(cachedDistribution)) {
1177
+ const { useCached } = await inquirer2.prompt([
1178
+ {
1179
+ type: "confirm",
1180
+ name: "useCached",
1181
+ message: `Use cached distribution "${cachedDistribution}"?`,
1182
+ default: true
1183
+ }
1184
+ ]);
1185
+ if (useCached) {
1186
+ logger.debug(`Using cached distribution: ${cachedDistribution}`);
1187
+ return cachedDistribution;
1188
+ }
1189
+ }
1190
+ const { distribution } = await inquirer2.prompt([
1191
+ {
1192
+ type: "list",
1193
+ name: "distribution",
1194
+ message: "Select WSL distribution:",
1195
+ choices: distributions.map((d) => ({ name: d, value: d }))
1196
+ }
1197
+ ]);
1198
+ setWslDistribution(distribution);
1199
+ logger.debug(`Selected distribution: ${distribution}`);
1200
+ return distribution;
1201
+ }
1202
+
1203
+ // ts-src/prompts/panel.ts
1204
+ init_esm_shims();
1205
+
1206
+ // ts-src/core/presets.ts
1207
+ init_esm_shims();
1208
+ init_errors();
1209
+ function savePreset(name, preset) {
1210
+ const validated = PresetConfigSchema.parse(preset);
1211
+ const config = loadConfig();
1212
+ config.presets[name] = validated;
1213
+ saveConfig(config);
1214
+ }
1215
+ function loadPresets() {
1216
+ const config = loadConfig();
1217
+ return { ...config.presets };
1218
+ }
1219
+ function applyPreset(name) {
1220
+ const presets = loadPresets();
1221
+ const preset = presets[name];
1222
+ if (preset === void 0) {
1223
+ throw new BranchNexusError(
1224
+ `Preset not found: ${name}`,
1225
+ 7 /* VALIDATION_ERROR */,
1226
+ "Select an existing preset or create a new one."
1227
+ );
1228
+ }
1229
+ return preset;
1230
+ }
1231
+ function deletePreset(name) {
1232
+ const config = loadConfig();
1233
+ delete config.presets[name];
1234
+ saveConfig(config);
1235
+ }
1236
+ function renamePreset(oldName, newName) {
1237
+ const config = loadConfig();
1238
+ const preset = config.presets[oldName];
1239
+ if (preset === void 0) {
1240
+ throw new BranchNexusError(
1241
+ `Preset not found: ${oldName}`,
1242
+ 7 /* VALIDATION_ERROR */,
1243
+ "Choose an existing preset to rename."
1244
+ );
1245
+ }
1246
+ config.presets[newName] = preset;
1247
+ delete config.presets[oldName];
1248
+ saveConfig(config);
1249
+ }
1250
+ function presetExists(name) {
1251
+ const config = loadConfig();
1252
+ return name in config.presets;
1253
+ }
1254
+ function createPresetFromCurrentConfig(name) {
1255
+ const config = loadConfig();
1256
+ const preset = {
1257
+ layout: config.defaultLayout,
1258
+ panes: config.defaultPanes,
1259
+ cleanup: config.cleanupPolicy
1260
+ };
1261
+ savePreset(name, preset);
1262
+ return preset;
1263
+ }
1264
+
1265
+ // ts-src/utils/theme.ts
1266
+ init_esm_shims();
1267
+ var COLOR_PALETTES = {
1268
+ cyan: {
1269
+ name: "cyan",
1270
+ label: "Cyan",
1271
+ primary: chalk9.cyan,
1272
+ primaryBold: chalk9.cyan.bold,
1273
+ bg: chalk9.bgCyan.black.bold
1274
+ },
1275
+ green: {
1276
+ name: "green",
1277
+ label: "Ye\u015Fil",
1278
+ primary: chalk9.green,
1279
+ primaryBold: chalk9.green.bold,
1280
+ bg: chalk9.bgGreen.black.bold
1281
+ },
1282
+ magenta: {
1283
+ name: "magenta",
1284
+ label: "Magenta",
1285
+ primary: chalk9.magenta,
1286
+ primaryBold: chalk9.magenta.bold,
1287
+ bg: chalk9.bgMagenta.black.bold
1288
+ },
1289
+ blue: {
1290
+ name: "blue",
1291
+ label: "Mavi",
1292
+ primary: chalk9.blue,
1293
+ primaryBold: chalk9.blue.bold,
1294
+ bg: chalk9.bgBlue.black.bold
1295
+ },
1296
+ yellow: {
1297
+ name: "yellow",
1298
+ label: "Sar\u0131",
1299
+ primary: chalk9.yellow,
1300
+ primaryBold: chalk9.yellow.bold,
1301
+ bg: chalk9.bgYellow.black.bold
1302
+ },
1303
+ red: {
1304
+ name: "red",
1305
+ label: "K\u0131rm\u0131z\u0131",
1306
+ primary: chalk9.red,
1307
+ primaryBold: chalk9.red.bold,
1308
+ bg: chalk9.bgRed.black.bold
1309
+ }
1310
+ };
1311
+ var PALETTE_KEYS = Object.keys(COLOR_PALETTES);
1312
+ function getPalette(name) {
1313
+ return COLOR_PALETTES[name] ?? COLOR_PALETTES.cyan;
1314
+ }
1315
+ var theme = {
1316
+ colors: {
1317
+ primary: chalk9.cyan,
1318
+ success: chalk9.green,
1319
+ warning: chalk9.yellow,
1320
+ error: chalk9.red,
1321
+ info: chalk9.blue,
1322
+ muted: chalk9.gray,
1323
+ highlight: chalk9.white.bold,
1324
+ accent: chalk9.magenta
1325
+ }};
1326
+ function formatPreview(key, value) {
1327
+ return `${theme.colors.muted(key.padEnd(10))} ${theme.colors.highlight(value)}`;
1328
+ }
1329
+
1330
+ // ts-src/prompts/github-browser.ts
1331
+ init_esm_shims();
1332
+
1333
+ // ts-src/github/api.ts
1334
+ init_esm_shims();
1335
+ var GitHubClient = class {
1336
+ token;
1337
+ constructor(token) {
1338
+ this.token = token ?? process.env.BRANCHNEXUS_GH_TOKEN ?? "";
1339
+ }
1340
+ async fetch(endpoint) {
1341
+ if (this.token === "") {
1342
+ throw new Error("GitHub token is required. Set BRANCHNEXUS_GH_TOKEN env var.");
1343
+ }
1344
+ const response = await fetch(`https://api.github.com${endpoint}`, {
1345
+ headers: {
1346
+ Authorization: `Bearer ${this.token}`,
1347
+ Accept: "application/vnd.github.v3+json",
1348
+ "User-Agent": "BranchNexus/1.0"
1349
+ }
1350
+ });
1351
+ if (!response.ok) {
1352
+ const error = await response.text();
1353
+ throw new Error(`GitHub API error: ${response.status} ${error}`);
1354
+ }
1355
+ return response.json();
1356
+ }
1357
+ async listRepositories() {
1358
+ const data = await this.fetch("/user/repos?per_page=100&sort=updated");
1359
+ return data.map((repo) => ({
1360
+ fullName: repo.full_name,
1361
+ cloneUrl: repo.clone_url
1362
+ }));
1363
+ }
1364
+ async listBranches(owner, repo) {
1365
+ const data = await this.fetch(`/repos/${owner}/${repo}/branches?per_page=100`);
1366
+ const defaultBranch = await this.getDefaultBranch(owner, repo);
1367
+ return data.map((branch) => ({
1368
+ name: branch.name,
1369
+ isDefault: branch.name === defaultBranch
1370
+ }));
1371
+ }
1372
+ async getDefaultBranch(owner, repo) {
1373
+ const data = await this.fetch(`/repos/${owner}/${repo}`);
1374
+ return data.default_branch ?? "main";
1375
+ }
1376
+ };
1377
+
1378
+ // ts-src/prompts/github-browser.ts
1379
+ var ANSI_RE = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
1380
+ var CLR = "\x1B[K";
1381
+ function stripAnsi(str) {
1382
+ return str.replace(ANSI_RE, "").length;
1383
+ }
1384
+ function padToWidth(content, width) {
1385
+ const pad = Math.max(0, width - stripAnsi(content));
1386
+ return content + " ".repeat(pad);
1387
+ }
1388
+ function boxLine(content, width) {
1389
+ return "\u2551" + padToWidth(content, width) + "\u2551" + CLR;
1390
+ }
1391
+ function boxTop(title, width, pal) {
1392
+ const tPad = Math.floor((width - title.length) / 2);
1393
+ return "\u2554" + "\u2550".repeat(tPad) + pal.primaryBold(title) + "\u2550".repeat(width - tPad - title.length) + "\u2557" + CLR;
1394
+ }
1395
+ function boxMid(width) {
1396
+ return "\u2560" + "\u2550".repeat(width) + "\u2563" + CLR;
1397
+ }
1398
+ function boxBottom(width) {
1399
+ return "\u255A" + "\u2550".repeat(width) + "\u255D" + CLR;
1400
+ }
1401
+ var MAX_VISIBLE = 12;
1402
+ function renderBrowser(state, pal) {
1403
+ const W = 62;
1404
+ const lines = [];
1405
+ lines.push(boxTop(" GitHub Repositories ", W, pal));
1406
+ if (state.loading) {
1407
+ lines.push(boxLine("", W));
1408
+ lines.push(boxLine(" " + chalk9.dim("Y\xFCkleniyor..."), W));
1409
+ lines.push(boxLine("", W));
1410
+ lines.push(boxBottom(W));
1411
+ return lines.join("\n");
1412
+ }
1413
+ if (state.error !== "") {
1414
+ lines.push(boxLine("", W));
1415
+ lines.push(boxLine(" " + chalk9.red(state.error), W));
1416
+ lines.push(boxLine("", W));
1417
+ lines.push(boxBottom(W));
1418
+ return lines.join("\n");
1419
+ }
1420
+ if (state.filtering) {
1421
+ const cursor = pal.primary(state.filterText + "\u2588");
1422
+ lines.push(boxLine(" " + chalk9.dim("Filtre: ") + cursor, W));
1423
+ } else if (state.filterText !== "") {
1424
+ lines.push(boxLine(" " + chalk9.dim("Filtre: ") + chalk9.white(state.filterText), W));
1425
+ }
1426
+ lines.push(boxMid(W));
1427
+ const repos = state.filteredRepos;
1428
+ if (repos.length === 0) {
1429
+ lines.push(boxLine("", W));
1430
+ lines.push(boxLine(" " + chalk9.dim("E\u015Fle\u015Fen repo bulunamad\u0131"), W));
1431
+ lines.push(boxLine("", W));
1432
+ } else {
1433
+ const start = state.scrollOffset;
1434
+ const end = Math.min(start + MAX_VISIBLE, repos.length);
1435
+ if (start > 0) {
1436
+ lines.push(boxLine(" " + chalk9.dim(` \u2191 ${start} daha...`), W));
1437
+ }
1438
+ for (let i = start; i < end; i++) {
1439
+ const focused = i === state.selectedIndex;
1440
+ const ptr = focused ? pal.primary("\u276F") : " ";
1441
+ const name = repos[i].fullName;
1442
+ const display = name.length > 50 ? "..." + name.slice(-47) : name;
1443
+ const label = focused ? pal.primaryBold(display) : chalk9.white(display);
1444
+ lines.push(boxLine(` ${ptr} ${label}`, W));
1445
+ }
1446
+ if (end < repos.length) {
1447
+ lines.push(boxLine(" " + chalk9.dim(` \u2193 ${repos.length - end} daha...`), W));
1448
+ }
1449
+ }
1450
+ lines.push(boxLine("", W));
1451
+ lines.push(boxMid(W));
1452
+ const count = chalk9.dim(`${repos.length} repo`);
1453
+ const help = state.filtering ? "Yaz\u0131n... Enter Onayla Esc \u0130ptal" : "\u2191\u2193 Gezin / Filtrele Enter Se\xE7 Esc \xC7\u0131k\u0131\u015F";
1454
+ lines.push(boxLine(" " + chalk9.dim(help) + " " + count, W));
1455
+ lines.push(boxBottom(W));
1456
+ return lines.join("\n");
1457
+ }
1458
+ async function showGitHubBrowser(token) {
1459
+ const config = loadConfig();
1460
+ const pal = getPalette(config.colorTheme);
1461
+ const state = {
1462
+ repos: [],
1463
+ filteredRepos: [],
1464
+ selectedIndex: 0,
1465
+ scrollOffset: 0,
1466
+ filterText: "",
1467
+ filtering: false,
1468
+ loading: true,
1469
+ error: ""
1470
+ };
1471
+ return new Promise((resolve2) => {
1472
+ let first = true;
1473
+ function draw() {
1474
+ process.stdout.write(first ? "\x1B[2J\x1B[H" : "\x1B[H");
1475
+ first = false;
1476
+ process.stdout.write(
1477
+ state.filtering ? "\x1B[?25h" : "\x1B[?25l"
1478
+ );
1479
+ process.stdout.write(renderBrowser(state, pal) + "\x1B[J\n");
1480
+ }
1481
+ readline.emitKeypressEvents(process.stdin);
1482
+ const wasRaw = process.stdin.isRaw ?? false;
1483
+ if (process.stdin.isTTY === true) {
1484
+ process.stdin.setRawMode(true);
1485
+ }
1486
+ process.stdin.resume();
1487
+ function teardown() {
1488
+ process.stdin.removeListener("keypress", onKey);
1489
+ if (process.stdin.isTTY === true) {
1490
+ process.stdin.setRawMode(wasRaw);
1491
+ }
1492
+ process.stdout.write("\x1B[?25h");
1493
+ process.stdin.pause();
1494
+ }
1495
+ function done(result) {
1496
+ teardown();
1497
+ console.clear();
1498
+ resolve2(result);
1499
+ }
1500
+ function applyFilter() {
1501
+ if (state.filterText === "") {
1502
+ state.filteredRepos = [...state.repos];
1503
+ } else {
1504
+ const lower = state.filterText.toLowerCase();
1505
+ state.filteredRepos = state.repos.filter(
1506
+ (r) => r.fullName.toLowerCase().includes(lower)
1507
+ );
1508
+ }
1509
+ state.selectedIndex = 0;
1510
+ state.scrollOffset = 0;
1511
+ }
1512
+ function ensureVisible() {
1513
+ if (state.selectedIndex < state.scrollOffset) {
1514
+ state.scrollOffset = state.selectedIndex;
1515
+ }
1516
+ if (state.selectedIndex >= state.scrollOffset + MAX_VISIBLE) {
1517
+ state.scrollOffset = state.selectedIndex - MAX_VISIBLE + 1;
1518
+ }
1519
+ }
1520
+ function onKey(str, key) {
1521
+ if (key === void 0 || key === null) return;
1522
+ if (key.ctrl === true && key.name === "c") {
1523
+ done(null);
1524
+ return;
1525
+ }
1526
+ if (state.loading) return;
1527
+ if (state.filtering) {
1528
+ if (key.name === "return") {
1529
+ state.filtering = false;
1530
+ draw();
1531
+ return;
1532
+ }
1533
+ if (key.name === "escape") {
1534
+ state.filtering = false;
1535
+ state.filterText = "";
1536
+ applyFilter();
1537
+ draw();
1538
+ return;
1539
+ }
1540
+ if (key.name === "backspace") {
1541
+ state.filterText = state.filterText.slice(0, -1);
1542
+ applyFilter();
1543
+ draw();
1544
+ return;
1545
+ }
1546
+ if (key.ctrl === true && key.name === "u") {
1547
+ state.filterText = "";
1548
+ applyFilter();
1549
+ draw();
1550
+ return;
1551
+ }
1552
+ if (str !== void 0 && str !== "" && key.ctrl !== true && key.meta !== true) {
1553
+ state.filterText += str;
1554
+ applyFilter();
1555
+ draw();
1556
+ }
1557
+ return;
1558
+ }
1559
+ if (key.name === "escape") {
1560
+ done(null);
1561
+ return;
1562
+ }
1563
+ if (key.name === "up") {
1564
+ if (state.selectedIndex > 0) {
1565
+ state.selectedIndex--;
1566
+ ensureVisible();
1567
+ }
1568
+ draw();
1569
+ return;
1570
+ }
1571
+ if (key.name === "down") {
1572
+ if (state.selectedIndex < state.filteredRepos.length - 1) {
1573
+ state.selectedIndex++;
1574
+ ensureVisible();
1575
+ }
1576
+ draw();
1577
+ return;
1578
+ }
1579
+ if (str === "/") {
1580
+ state.filtering = true;
1581
+ draw();
1582
+ return;
1583
+ }
1584
+ if (key.name === "return") {
1585
+ if (state.filteredRepos.length > 0) {
1586
+ const repo = state.filteredRepos[state.selectedIndex];
1587
+ done({ cloneUrl: repo.cloneUrl, fullName: repo.fullName });
1588
+ }
1589
+ return;
1590
+ }
1591
+ }
1592
+ process.stdin.on("keypress", onKey);
1593
+ draw();
1594
+ const client = new GitHubClient(token);
1595
+ client.listRepositories().then((repos) => {
1596
+ state.repos = repos;
1597
+ state.filteredRepos = [...repos];
1598
+ state.loading = false;
1599
+ updateGithubRepoCache(
1600
+ repos.map((r) => ({ full_name: r.fullName, clone_url: r.cloneUrl }))
1601
+ );
1602
+ draw();
1603
+ }).catch((error) => {
1604
+ state.loading = false;
1605
+ state.error = error instanceof Error ? error.message : String(error);
1606
+ draw();
1607
+ });
1608
+ });
1609
+ }
1610
+
1611
+ // ts-src/prompts/panel.ts
1612
+ var LAYOUTS = [
1613
+ { value: "grid", label: "Grid (2x2)" },
1614
+ { value: "horizontal", label: "Yatay" },
1615
+ { value: "vertical", label: "Dikey" }
1616
+ ];
1617
+ var PANE_OPTIONS = [1, 2, 3, 4, 5, 6];
1618
+ var CLEANUP_OPTIONS = [
1619
+ { value: "session", label: "Session" },
1620
+ { value: "persistent", label: "Persistent" }
1621
+ ];
1622
+ var FIELD = {
1623
+ HAS_TOKEN: 0,
1624
+ TOKEN: 1,
1625
+ REPO: 2,
1626
+ LAYOUT: 3,
1627
+ PANES: 4,
1628
+ CLEANUP: 5,
1629
+ COLOR: 6,
1630
+ SUBMIT: 7
1631
+ };
1632
+ var COLOR_OPTIONS = PALETTE_KEYS.map((key) => ({
1633
+ value: key,
1634
+ label: COLOR_PALETTES[key].label
1635
+ }));
1636
+ var ANSI_RE2 = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
1637
+ var CLR2 = "\x1B[K";
1638
+ function stripAnsi2(str) {
1639
+ return str.replace(ANSI_RE2, "");
1640
+ }
1641
+ function visibleLength(str) {
1642
+ return stripAnsi2(str).length;
1643
+ }
1644
+ function padToWidth2(content, width) {
1645
+ const pad = Math.max(0, width - visibleLength(content));
1646
+ return content + " ".repeat(pad);
1647
+ }
1648
+ function boxLine2(content, width) {
1649
+ return "\u2551" + padToWidth2(content, width) + "\u2551" + CLR2;
1650
+ }
1651
+ function boxTop2(title, width, pal) {
1652
+ const tPad = Math.floor((width - title.length) / 2);
1653
+ return "\u2554" + "\u2550".repeat(tPad) + pal.primaryBold(title) + "\u2550".repeat(width - tPad - title.length) + "\u2557" + CLR2;
1654
+ }
1655
+ function boxMid2(width) {
1656
+ return "\u2560" + "\u2550".repeat(width) + "\u2563" + CLR2;
1657
+ }
1658
+ function boxBottom2(width) {
1659
+ return "\u255A" + "\u2550".repeat(width) + "\u255D" + CLR2;
1660
+ }
1661
+ function selectDisplay(label, focused, pal) {
1662
+ if (focused) {
1663
+ return chalk9.dim("\u25C0 ") + pal.primaryBold(label) + chalk9.dim(" \u25B6");
1664
+ }
1665
+ return chalk9.white(label);
1666
+ }
1667
+ function renderButton(label, focused, width, pal) {
1668
+ const btn = focused ? pal.bg(` \u25B6 ${label} `) : chalk9.dim(` \u25B6 ${label} `);
1669
+ const btnLen = visibleLength(btn);
1670
+ const left = Math.floor((width - btnLen) / 2);
1671
+ const right = Math.max(0, width - left - btnLen);
1672
+ return "\u2551" + " ".repeat(left) + btn + " ".repeat(right) + "\u2551" + CLR2;
1673
+ }
1674
+ function attachKeypress(handler) {
1675
+ readline.emitKeypressEvents(process.stdin);
1676
+ const wasRaw = process.stdin.isRaw ?? false;
1677
+ if (process.stdin.isTTY === true) {
1678
+ process.stdin.setRawMode(true);
1679
+ }
1680
+ process.stdin.resume();
1681
+ function onKey(str, key) {
1682
+ if (key === void 0 || key === null) return;
1683
+ if (key.ctrl === true && key.name === "c") {
1684
+ teardown();
1685
+ process.exit(0);
1686
+ }
1687
+ handler(str, key);
1688
+ }
1689
+ function teardown() {
1690
+ process.stdin.removeListener("keypress", onKey);
1691
+ if (process.stdin.isTTY === true) {
1692
+ process.stdin.setRawMode(wasRaw);
1693
+ }
1694
+ process.stdout.write("\x1B[?25h");
1695
+ process.stdin.pause();
1696
+ }
1697
+ process.stdin.on("keypress", onKey);
1698
+ return { teardown };
1699
+ }
1700
+ function createDrawFn(renderFn) {
1701
+ let first = true;
1702
+ return (editing) => {
1703
+ process.stdout.write(first ? "\x1B[2J\x1B[H" : "\x1B[H");
1704
+ first = false;
1705
+ process.stdout.write(editing ? "\x1B[?25h" : "\x1B[?25l");
1706
+ process.stdout.write(renderFn() + "\x1B[J\n");
1707
+ };
1708
+ }
1709
+ function getPanelNav(state) {
1710
+ const fields = [FIELD.HAS_TOKEN];
1711
+ if (state.hasToken) {
1712
+ fields.push(FIELD.TOKEN);
1713
+ }
1714
+ fields.push(FIELD.REPO, FIELD.LAYOUT, FIELD.PANES, FIELD.CLEANUP, FIELD.COLOR, FIELD.SUBMIT);
1715
+ return fields;
1716
+ }
1717
+ function renderPanel(state) {
1718
+ const W = 58;
1719
+ const LBL_W = 17;
1720
+ const lines = [];
1721
+ const pal = getPalette(COLOR_OPTIONS[state.colorIndex].value);
1722
+ function fRow(id, label, value) {
1723
+ const focused = state.focusIndex === id;
1724
+ const ptr = focused ? pal.primary("\u276F") : " ";
1725
+ const lbl = focused ? pal.primaryBold(label.padEnd(LBL_W)) : chalk9.white(label.padEnd(LBL_W));
1726
+ return boxLine2(` ${ptr} ${lbl} ${value}`, W);
1727
+ }
1728
+ lines.push(boxTop2(" BranchNexus ", W, pal));
1729
+ lines.push(boxLine2(" " + chalk9.dim("Multi-Branch Workspace Manager"), W));
1730
+ lines.push(boxMid2(W));
1731
+ lines.push(boxLine2("", W));
1732
+ const tokLabel = state.hasToken ? "Evet" : "Hay\u0131r";
1733
+ lines.push(fRow(FIELD.HAS_TOKEN, "GitHub Token", selectDisplay(tokLabel, state.focusIndex === FIELD.HAS_TOKEN, pal)));
1734
+ if (state.hasToken) {
1735
+ let tv;
1736
+ if (state.focusIndex === FIELD.TOKEN && state.editing) {
1737
+ tv = pal.primary(state.editBuffer + "\u2588");
1738
+ } else if (state.token !== "") {
1739
+ tv = chalk9.dim("*".repeat(Math.min(state.token.length, 20)));
1740
+ } else {
1741
+ tv = chalk9.dim("(Enter ile girin)");
1742
+ }
1743
+ lines.push(fRow(FIELD.TOKEN, " Token", tv));
1744
+ }
1745
+ let rv;
1746
+ if (state.focusIndex === FIELD.REPO && state.editing) {
1747
+ const buf = state.editBuffer;
1748
+ rv = pal.primary((buf.length > 28 ? "..." + buf.slice(-25) : buf) + "\u2588");
1749
+ } else if (state.repoUrl !== "") {
1750
+ const s = state.repoUrl;
1751
+ rv = chalk9.white(s.length > 30 ? s.slice(0, 27) + "..." : s);
1752
+ } else {
1753
+ rv = chalk9.dim("(Enter ile girin)");
1754
+ }
1755
+ lines.push(fRow(FIELD.REPO, "Repository URL", rv));
1756
+ lines.push(fRow(FIELD.LAYOUT, "Layout", selectDisplay(LAYOUTS[state.layoutIndex].label, state.focusIndex === FIELD.LAYOUT, pal)));
1757
+ lines.push(fRow(FIELD.PANES, "Pane Say\u0131s\u0131", selectDisplay(String(PANE_OPTIONS[state.panesIndex]), state.focusIndex === FIELD.PANES, pal)));
1758
+ lines.push(fRow(FIELD.CLEANUP, "Cleanup", selectDisplay(CLEANUP_OPTIONS[state.cleanupIndex].label, state.focusIndex === FIELD.CLEANUP, pal)));
1759
+ const colorLabel = COLOR_OPTIONS[state.colorIndex].label;
1760
+ const colorPreview = pal.primary("\u25A0 ") + colorLabel;
1761
+ lines.push(fRow(FIELD.COLOR, "Renk", selectDisplay(colorPreview, state.focusIndex === FIELD.COLOR, pal)));
1762
+ lines.push(boxLine2("", W));
1763
+ if (state.error !== "") {
1764
+ lines.push(boxLine2(" " + chalk9.red("\u26A0 " + state.error), W));
1765
+ lines.push(boxLine2("", W));
1766
+ }
1767
+ lines.push(renderButton("BA\u015ELAT", state.focusIndex === FIELD.SUBMIT, W, pal));
1768
+ lines.push(boxLine2("", W));
1769
+ lines.push(boxMid2(W));
1770
+ const help = state.editing ? "Yaz\u0131n... Enter Onayla Esc \u0130ptal Ctrl+U Temizle" : "\u2191\u2193 Gezin \u2190\u2192 De\u011Fi\u015Ftir Enter D\xFCzenle P Preset G GitHub Esc \xC7\u0131k\u0131\u015F";
1771
+ lines.push(boxLine2(" " + chalk9.dim(help), W));
1772
+ lines.push(boxBottom2(W));
1773
+ return lines.join("\n");
1774
+ }
1775
+ async function showPanel() {
1776
+ const config = loadConfig();
1777
+ let layoutIdx = LAYOUTS.findIndex((l) => l.value === config.defaultLayout);
1778
+ if (layoutIdx < 0) layoutIdx = 0;
1779
+ let panesIdx = PANE_OPTIONS.indexOf(config.defaultPanes > 0 ? config.defaultPanes : 4);
1780
+ if (panesIdx < 0) panesIdx = 3;
1781
+ let cleanupIdx = CLEANUP_OPTIONS.findIndex((o) => o.value === config.cleanupPolicy);
1782
+ if (cleanupIdx < 0) cleanupIdx = 0;
1783
+ let colorIdx = PALETTE_KEYS.indexOf(config.colorTheme);
1784
+ if (colorIdx < 0) colorIdx = 0;
1785
+ const state = {
1786
+ focusIndex: FIELD.REPO,
1787
+ editing: false,
1788
+ editBuffer: "",
1789
+ error: "",
1790
+ hasToken: config.githubToken !== void 0 && config.githubToken !== "",
1791
+ token: "",
1792
+ repoUrl: "",
1793
+ layoutIndex: layoutIdx,
1794
+ panesIndex: panesIdx,
1795
+ cleanupIndex: cleanupIdx,
1796
+ colorIndex: colorIdx
1797
+ };
1798
+ return new Promise((resolve2) => {
1799
+ const draw = createDrawFn(() => renderPanel(state));
1800
+ const { teardown } = attachKeypress((str, key) => {
1801
+ state.error = "";
1802
+ if (state.editing) {
1803
+ panelEdit(str, key);
1804
+ } else {
1805
+ panelNav(str, key);
1806
+ }
1807
+ });
1808
+ function done(result) {
1809
+ teardown();
1810
+ console.clear();
1811
+ resolve2(result);
1812
+ }
1813
+ function panelEdit(str, key) {
1814
+ if (key.name === "return") {
1815
+ if (state.focusIndex === FIELD.TOKEN) state.token = state.editBuffer;
1816
+ else if (state.focusIndex === FIELD.REPO) state.repoUrl = state.editBuffer;
1817
+ state.editing = false;
1818
+ draw(false);
1819
+ return;
1820
+ }
1821
+ if (key.name === "escape") {
1822
+ state.editing = false;
1823
+ draw(false);
1824
+ return;
1825
+ }
1826
+ if (key.name === "backspace") {
1827
+ state.editBuffer = state.editBuffer.slice(0, -1);
1828
+ draw(true);
1829
+ return;
1830
+ }
1831
+ if (key.ctrl === true && key.name === "u") {
1832
+ state.editBuffer = "";
1833
+ draw(true);
1834
+ return;
1835
+ }
1836
+ if (str !== void 0 && str !== "" && key.ctrl !== true && key.meta !== true) {
1837
+ state.editBuffer += str;
1838
+ draw(true);
1839
+ }
1840
+ }
1841
+ function panelNav(str, key) {
1842
+ const nav = getPanelNav(state);
1843
+ const idx = nav.indexOf(state.focusIndex);
1844
+ if (key.name === "escape") {
1845
+ done(null);
1846
+ return;
1847
+ }
1848
+ if (key.name === "up" || key.name === "tab" && key.shift === true) {
1849
+ if (idx > 0) state.focusIndex = nav[idx - 1];
1850
+ draw(false);
1851
+ return;
1852
+ }
1853
+ if (key.name === "down" || key.name === "tab") {
1854
+ if (idx < nav.length - 1) state.focusIndex = nav[idx + 1];
1855
+ draw(false);
1856
+ return;
1857
+ }
1858
+ if (key.name === "left" || key.name === "right") {
1859
+ const d = key.name === "right" ? 1 : -1;
1860
+ panelCycle(d);
1861
+ draw(false);
1862
+ return;
1863
+ }
1864
+ if (key.name === "space") {
1865
+ panelCycle(1);
1866
+ draw(false);
1867
+ return;
1868
+ }
1869
+ if (str === "p" || str === "P") {
1870
+ const presets = loadPresets();
1871
+ const names = Object.keys(presets);
1872
+ if (names.length === 0) {
1873
+ state.error = "Kay\u0131tl\u0131 preset yok";
1874
+ draw(false);
1875
+ return;
1876
+ }
1877
+ const currentConfig = `${LAYOUTS[state.layoutIndex].value}-${PANE_OPTIONS[state.panesIndex]}-${CLEANUP_OPTIONS[state.cleanupIndex].value}`;
1878
+ let nextIdx = 0;
1879
+ for (let i = 0; i < names.length; i++) {
1880
+ const pr = presets[names[i]];
1881
+ const key2 = `${pr.layout}-${pr.panes}-${pr.cleanup}`;
1882
+ if (key2 === currentConfig && i < names.length - 1) {
1883
+ nextIdx = i + 1;
1884
+ break;
1885
+ }
1886
+ }
1887
+ const preset = applyPreset(names[nextIdx]);
1888
+ const li = LAYOUTS.findIndex((l) => l.value === preset.layout);
1889
+ if (li >= 0) state.layoutIndex = li;
1890
+ const pi = PANE_OPTIONS.indexOf(preset.panes);
1891
+ if (pi >= 0) state.panesIndex = pi;
1892
+ const ci = CLEANUP_OPTIONS.findIndex((o) => o.value === preset.cleanup);
1893
+ if (ci >= 0) state.cleanupIndex = ci;
1894
+ state.error = `Preset: ${names[nextIdx]}`;
1895
+ draw(false);
1896
+ return;
1897
+ }
1898
+ if (str === "g" || str === "G") {
1899
+ const token = state.hasToken && state.token !== "" ? state.token : loadConfig().githubToken;
1900
+ if (token === "") {
1901
+ state.error = "GitHub token gerekli";
1902
+ draw(false);
1903
+ return;
1904
+ }
1905
+ teardown();
1906
+ showGitHubBrowser(token).then((result) => {
1907
+ if (result !== null) {
1908
+ state.repoUrl = result.cloneUrl;
1909
+ }
1910
+ const reattached = attachKeypress((s, k) => {
1911
+ state.error = "";
1912
+ if (state.editing) {
1913
+ panelEdit(s, k);
1914
+ } else {
1915
+ panelNav(s, k);
1916
+ }
1917
+ });
1918
+ Object.assign({ teardown: reattached.teardown });
1919
+ draw(false);
1920
+ }).catch(() => {
1921
+ draw(false);
1922
+ });
1923
+ return;
1924
+ }
1925
+ if (key.name === "return") {
1926
+ panelEnter();
1927
+ return;
1928
+ }
1929
+ }
1930
+ function panelCycle(d) {
1931
+ switch (state.focusIndex) {
1932
+ case FIELD.HAS_TOKEN:
1933
+ state.hasToken = !state.hasToken;
1934
+ break;
1935
+ case FIELD.LAYOUT:
1936
+ state.layoutIndex = (state.layoutIndex + d + LAYOUTS.length) % LAYOUTS.length;
1937
+ break;
1938
+ case FIELD.PANES:
1939
+ state.panesIndex = (state.panesIndex + d + PANE_OPTIONS.length) % PANE_OPTIONS.length;
1940
+ break;
1941
+ case FIELD.CLEANUP:
1942
+ state.cleanupIndex = (state.cleanupIndex + d + CLEANUP_OPTIONS.length) % CLEANUP_OPTIONS.length;
1943
+ break;
1944
+ case FIELD.COLOR:
1945
+ state.colorIndex = (state.colorIndex + d + COLOR_OPTIONS.length) % COLOR_OPTIONS.length;
1946
+ break;
1947
+ }
1948
+ }
1949
+ function panelEnter() {
1950
+ if (state.focusIndex === FIELD.TOKEN || state.focusIndex === FIELD.REPO) {
1951
+ state.editing = true;
1952
+ state.editBuffer = state.focusIndex === FIELD.TOKEN ? state.token : state.repoUrl;
1953
+ draw(true);
1954
+ return;
1955
+ }
1956
+ if (state.focusIndex === FIELD.HAS_TOKEN) {
1957
+ state.hasToken = !state.hasToken;
1958
+ draw(false);
1959
+ return;
1960
+ }
1961
+ if (state.focusIndex === FIELD.SUBMIT) {
1962
+ if (state.repoUrl.trim() === "") {
1963
+ state.error = "Repository URL gerekli";
1964
+ state.focusIndex = FIELD.REPO;
1965
+ draw(false);
1966
+ return;
1967
+ }
1968
+ const url = state.repoUrl.trim();
1969
+ if (!url.includes("github.com") && !url.includes("gitlab.com") && !url.includes("bitbucket.org")) {
1970
+ state.error = "Ge\xE7erli bir Git URL girin";
1971
+ state.focusIndex = FIELD.REPO;
1972
+ draw(false);
1973
+ return;
1974
+ }
1975
+ if (state.hasToken && state.token !== "") {
1976
+ setGithubToken(state.token);
1977
+ }
1978
+ const selectedColor = COLOR_OPTIONS[state.colorIndex].value;
1979
+ const cfg = loadConfig();
1980
+ cfg.colorTheme = selectedColor;
1981
+ saveConfig(cfg);
1982
+ const urls = url.split(",").map((u) => u.trim()).filter((u) => u !== "");
1983
+ done({
1984
+ token: state.hasToken && state.token !== "" ? state.token : null,
1985
+ repoUrl: urls[0],
1986
+ repoUrls: urls,
1987
+ layout: LAYOUTS[state.layoutIndex].value,
1988
+ paneCount: PANE_OPTIONS[state.panesIndex],
1989
+ cleanup: CLEANUP_OPTIONS[state.cleanupIndex].value,
1990
+ colorTheme: selectedColor
1991
+ });
1992
+ return;
1993
+ }
1994
+ draw(false);
1995
+ }
1996
+ draw(false);
1997
+ });
1998
+ }
1999
+ var RANDOM_NAMES = [
2000
+ "Alpha",
2001
+ "Beta",
2002
+ "Gamma",
2003
+ "Delta",
2004
+ "Epsilon",
2005
+ "Zeta",
2006
+ "Nova",
2007
+ "Orion",
2008
+ "Vega",
2009
+ "Atlas",
2010
+ "Comet",
2011
+ "Pulsar",
2012
+ "Spark",
2013
+ "Blaze",
2014
+ "Storm",
2015
+ "Frost",
2016
+ "Lunar",
2017
+ "Solar"
2018
+ ];
2019
+ function pickRandomNames(count) {
2020
+ const shuffled = [...RANDOM_NAMES].sort(() => Math.random() - 0.5);
2021
+ return shuffled.slice(0, count);
2022
+ }
2023
+ function renderBranchForm(state, branches, paneCount, pal) {
2024
+ const W = 62;
2025
+ const lines = [];
2026
+ const submitIdx = paneCount;
2027
+ lines.push(boxTop2(" Branch Se\xE7imi ", W, pal));
2028
+ lines.push(boxLine2(" " + chalk9.dim("\u2190\u2192 Branch N \u0130sim C Komut"), W));
2029
+ lines.push(boxMid2(W));
2030
+ lines.push(boxLine2("", W));
2031
+ const usageCount = /* @__PURE__ */ new Map();
2032
+ for (const bi of state.branchIndices) {
2033
+ usageCount.set(bi, (usageCount.get(bi) ?? 0) + 1);
2034
+ }
2035
+ for (let i = 0; i < paneCount; i++) {
2036
+ const focused = state.focusIndex === i;
2037
+ const ptr = focused ? pal.primary("\u276F") : " ";
2038
+ if (focused && state.editing) {
2039
+ const editLabel = pal.primaryBold("\u0130sim: ");
2040
+ const cursor = pal.primary(state.editBuffer + "\u2588");
2041
+ lines.push(boxLine2(` ${ptr} ${editLabel}${cursor}`, W));
2042
+ continue;
2043
+ }
2044
+ if (focused && state.editingCommand) {
2045
+ const editLabel = pal.primaryBold("Cmd: ");
2046
+ const cursor = pal.primary(state.editBuffer + "\u2588");
2047
+ lines.push(boxLine2(` ${ptr} ${editLabel}${cursor}`, W));
2048
+ continue;
2049
+ }
2050
+ const name = state.paneNames[i];
2051
+ const nameDisplay = focused ? pal.primaryBold(name.padEnd(10)) : chalk9.white(name.padEnd(10));
2052
+ const branchName = branches[state.branchIndices[i]];
2053
+ const shortBranch = branchName.length > 22 ? "..." + branchName.slice(-19) : branchName;
2054
+ const isDuplicate = (usageCount.get(state.branchIndices[i]) ?? 0) > 1;
2055
+ const dupHint = isDuplicate ? chalk9.yellow(" +fork") : "";
2056
+ const cmdHint = state.startupCommands[i] !== "" ? chalk9.dim(" $") : "";
2057
+ const branchDisplay = selectDisplay(shortBranch, focused, pal) + dupHint + cmdHint;
2058
+ lines.push(boxLine2(` ${ptr} ${nameDisplay} ${chalk9.dim("\u2502")} ${branchDisplay}`, W));
2059
+ }
2060
+ lines.push(boxLine2("", W));
2061
+ if (state.error !== "") {
2062
+ lines.push(boxLine2(" " + chalk9.red("\u26A0 " + state.error), W));
2063
+ lines.push(boxLine2("", W));
2064
+ }
2065
+ lines.push(renderButton("ONAYLA", state.focusIndex === submitIdx, W, pal));
2066
+ lines.push(boxLine2("", W));
2067
+ lines.push(boxMid2(W));
2068
+ const help = state.editing || state.editingCommand ? "Yaz\u0131n... Enter Onayla Esc \u0130ptal Ctrl+U Temizle" : "\u2191\u2193 Gezin \u2190\u2192 Branch N \u0130sim C Komut Enter Onayla Esc \xC7\u0131k\u0131\u015F";
2069
+ lines.push(boxLine2(" " + chalk9.dim(help), W));
2070
+ lines.push(boxBottom2(W));
2071
+ return lines.join("\n");
2072
+ }
2073
+ async function showBranchSelection(availableBranches, paneCount) {
2074
+ const submitIdx = paneCount;
2075
+ const initialIndices = [];
2076
+ for (let i = 0; i < paneCount; i++) {
2077
+ initialIndices.push(i % availableBranches.length);
2078
+ }
2079
+ const randomNames = pickRandomNames(paneCount);
2080
+ const state = {
2081
+ focusIndex: 0,
2082
+ branchIndices: initialIndices,
2083
+ paneNames: randomNames,
2084
+ startupCommands: Array.from({ length: paneCount }, () => ""),
2085
+ editing: false,
2086
+ editingCommand: false,
2087
+ editBuffer: "",
2088
+ error: ""
2089
+ };
2090
+ const pal = getPalette(loadConfig().colorTheme);
2091
+ return new Promise((resolve2) => {
2092
+ const draw = createDrawFn(() => renderBranchForm(state, availableBranches, paneCount, pal));
2093
+ const { teardown } = attachKeypress((str, key) => {
2094
+ state.error = "";
2095
+ if (state.editing) {
2096
+ if (key.name === "return") {
2097
+ const val = state.editBuffer.trim();
2098
+ state.paneNames[state.focusIndex] = val !== "" ? val : randomNames[state.focusIndex];
2099
+ state.editing = false;
2100
+ draw(false);
2101
+ return;
2102
+ }
2103
+ if (key.name === "escape") {
2104
+ state.editing = false;
2105
+ draw(false);
2106
+ return;
2107
+ }
2108
+ if (key.name === "backspace") {
2109
+ state.editBuffer = state.editBuffer.slice(0, -1);
2110
+ draw(true);
2111
+ return;
2112
+ }
2113
+ if (key.ctrl === true && key.name === "u") {
2114
+ state.editBuffer = "";
2115
+ draw(true);
2116
+ return;
2117
+ }
2118
+ if (str !== void 0 && str !== "" && key.ctrl !== true && key.meta !== true) {
2119
+ state.editBuffer += str;
2120
+ draw(true);
2121
+ }
2122
+ return;
2123
+ }
2124
+ if (state.editingCommand) {
2125
+ if (key.name === "return") {
2126
+ state.startupCommands[state.focusIndex] = state.editBuffer.trim();
2127
+ state.editingCommand = false;
2128
+ draw(false);
2129
+ return;
2130
+ }
2131
+ if (key.name === "escape") {
2132
+ state.editingCommand = false;
2133
+ draw(false);
2134
+ return;
2135
+ }
2136
+ if (key.name === "backspace") {
2137
+ state.editBuffer = state.editBuffer.slice(0, -1);
2138
+ draw(true);
2139
+ return;
2140
+ }
2141
+ if (key.ctrl === true && key.name === "u") {
2142
+ state.editBuffer = "";
2143
+ draw(true);
2144
+ return;
2145
+ }
2146
+ if (str !== void 0 && str !== "" && key.ctrl !== true && key.meta !== true) {
2147
+ state.editBuffer += str;
2148
+ draw(true);
2149
+ }
2150
+ return;
2151
+ }
2152
+ if (key.name === "escape") {
2153
+ teardown();
2154
+ console.clear();
2155
+ resolve2(null);
2156
+ return;
2157
+ }
2158
+ if (key.name === "up" || key.name === "tab" && key.shift === true) {
2159
+ if (state.focusIndex > 0) state.focusIndex--;
2160
+ draw(false);
2161
+ return;
2162
+ }
2163
+ if (key.name === "down" || key.name === "tab") {
2164
+ if (state.focusIndex < submitIdx) state.focusIndex++;
2165
+ draw(false);
2166
+ return;
2167
+ }
2168
+ if ((str === "n" || str === "N") && state.focusIndex < paneCount) {
2169
+ state.editing = true;
2170
+ state.editBuffer = state.paneNames[state.focusIndex];
2171
+ draw(true);
2172
+ return;
2173
+ }
2174
+ if ((str === "c" || str === "C") && state.focusIndex < paneCount) {
2175
+ state.editingCommand = true;
2176
+ state.editBuffer = state.startupCommands[state.focusIndex];
2177
+ draw(true);
2178
+ return;
2179
+ }
2180
+ if (key.name === "left" || key.name === "right") {
2181
+ if (state.focusIndex < paneCount) {
2182
+ const d = key.name === "right" ? 1 : -1;
2183
+ const cur = state.branchIndices[state.focusIndex];
2184
+ state.branchIndices[state.focusIndex] = (cur + d + availableBranches.length) % availableBranches.length;
2185
+ }
2186
+ draw(false);
2187
+ return;
2188
+ }
2189
+ if (key.name === "space" && state.focusIndex < paneCount) {
2190
+ const cur = state.branchIndices[state.focusIndex];
2191
+ state.branchIndices[state.focusIndex] = (cur + 1) % availableBranches.length;
2192
+ draw(false);
2193
+ return;
2194
+ }
2195
+ if (key.name === "return") {
2196
+ if (state.focusIndex === submitIdx) {
2197
+ const selected = state.branchIndices.map((bi) => availableBranches[bi]);
2198
+ teardown();
2199
+ console.clear();
2200
+ resolve2({
2201
+ branches: selected,
2202
+ paneNames: [...state.paneNames],
2203
+ startupCommands: [...state.startupCommands]
2204
+ });
2205
+ return;
2206
+ }
2207
+ if (state.focusIndex < paneCount) {
2208
+ const cur = state.branchIndices[state.focusIndex];
2209
+ state.branchIndices[state.focusIndex] = (cur + 1) % availableBranches.length;
2210
+ draw(false);
2211
+ }
2212
+ }
2213
+ });
2214
+ draw(false);
2215
+ });
2216
+ }
2217
+ function showPreview(data) {
2218
+ console.log();
2219
+ console.log(chalk9.dim("\u2500".repeat(50)));
2220
+ console.log(chalk9.bold("\u{1F4CB} \xD6N\u0130ZLEME"));
2221
+ console.log(chalk9.dim("\u2500".repeat(50)));
2222
+ console.log();
2223
+ console.log(formatPreview("Repo", data.repoUrl));
2224
+ console.log(formatPreview("Layout", data.layout.toUpperCase()));
2225
+ console.log(formatPreview("Branches", `${data.branchCount} adet`));
2226
+ data.branches.forEach((b, i) => {
2227
+ console.log(chalk9.dim(` ${i + 1}. ${b}`));
2228
+ });
2229
+ console.log(formatPreview("Cleanup", data.cleanup === "session" ? "Session" : "Persistent"));
2230
+ console.log();
2231
+ }
2232
+ async function confirmStart() {
2233
+ const confirm3 = await p3.confirm({
2234
+ message: "Ba\u015Flatmak i\xE7in onayla",
2235
+ initialValue: true
2236
+ });
2237
+ return confirm3 === true;
2238
+ }
2239
+ function showSuccess(message) {
2240
+ p3.outro(chalk9.green("\u2713 ") + message);
2241
+ }
2242
+ function showError(message, hint) {
2243
+ console.log();
2244
+ console.log(chalk9.red("\u2715 ") + message);
2245
+ if (hint !== void 0 && hint !== "") {
2246
+ console.log(chalk9.dim(" \u2192 " + hint));
2247
+ }
2248
+ }
2249
+
2250
+ // ts-src/prompts/cleanup.ts
2251
+ init_esm_shims();
2252
+ async function promptCleanup(dirtyPaths) {
2253
+ console.log();
2254
+ console.log(chalk9.yellow("\u26A0 Kaydedilmemi\u015F de\u011Fi\u015Fiklikler tespit edildi:"));
2255
+ console.log();
2256
+ for (const path3 of dirtyPaths) {
2257
+ const short = path3.split("/").slice(-2).join("/");
2258
+ console.log(chalk9.dim(` \u25CF ${short}`));
2259
+ }
2260
+ console.log();
2261
+ const choice = await p3.select({
2262
+ message: "Ne yapmak istersiniz?",
2263
+ options: [
2264
+ {
2265
+ value: "Koruyarak Cik" /* PRESERVE */,
2266
+ label: "Koruyarak \xC7\u0131k",
2267
+ hint: "Dirty worktree'ler korunur, temizler silinir"
2268
+ },
2269
+ {
2270
+ value: "Temizleyerek Cik" /* CLEAN */,
2271
+ label: "Temizleyerek \xC7\u0131k",
2272
+ hint: "T\xFCm worktree'ler silinir (de\u011Fi\u015Fiklikler kaybolur!)"
2273
+ },
2274
+ {
2275
+ value: "Vazgec" /* CANCEL */,
2276
+ label: "Vazge\xE7",
2277
+ hint: "Hi\xE7bir \u015Fey yapma"
2278
+ }
2279
+ ]
2280
+ });
2281
+ if (p3.isCancel(choice)) {
2282
+ return "Vazgec" /* CANCEL */;
2283
+ }
2284
+ return choice;
2285
+ }
2286
+
2287
+ // ts-src/commands/run.ts
2288
+ init_logger();
2289
+
2290
+ // ts-src/hooks/runner.ts
2291
+ init_esm_shims();
2292
+ init_logger();
2293
+ var HookRunner = class {
2294
+ timeoutSeconds;
2295
+ trustedConfig;
2296
+ allowCommandPrefixes;
2297
+ constructor(options) {
2298
+ this.timeoutSeconds = options?.timeoutSeconds ?? 30;
2299
+ this.trustedConfig = options?.trustedConfig ?? true;
2300
+ this.allowCommandPrefixes = options?.allowCommandPrefixes ?? [];
2301
+ }
2302
+ isCommandAllowed(command) {
2303
+ if (this.trustedConfig) {
2304
+ return true;
2305
+ }
2306
+ const argv = command.split(/\s+/);
2307
+ if (argv.length === 0) {
2308
+ return false;
2309
+ }
2310
+ if (this.allowCommandPrefixes.length === 0) {
2311
+ return false;
2312
+ }
2313
+ return this.allowCommandPrefixes.includes(argv[0]);
2314
+ }
2315
+ async run(pane, commands, distribution) {
2316
+ const executions = [];
2317
+ logger.debug(`Running ${commands.length} hook commands for pane=${pane}`);
2318
+ for (const command of commands) {
2319
+ if (!this.isCommandAllowed(command)) {
2320
+ logger.warn(`Hook command blocked by policy pane=${pane} command=${command}`);
2321
+ executions.push({
2322
+ command,
2323
+ success: false,
2324
+ returncode: 126,
2325
+ output: "Command blocked by hook trust policy."
2326
+ });
2327
+ continue;
2328
+ }
2329
+ try {
2330
+ logger.debug(`Executing hook command pane=${pane} command=${command}`);
2331
+ const isWindows = detectPlatform() === "windows" /* WINDOWS */;
2332
+ const cmd = ["bash", "-lc", command];
2333
+ const finalCmd = isWindows && distribution !== void 0 && distribution !== "" ? buildWslCommand(distribution, cmd) : cmd;
2334
+ const result = await execa(finalCmd[0], finalCmd.slice(1), {
2335
+ timeout: this.timeoutSeconds * 1e3,
2336
+ reject: false,
2337
+ all: true
2338
+ });
2339
+ const success = result.exitCode === 0;
2340
+ const output = `${result.stdout}${result.stderr}`.trim();
2341
+ if (!success) {
2342
+ logger.warn(
2343
+ `Hook command failed pane=${pane} returncode=${result.exitCode} command=${command}`
2344
+ );
2345
+ }
2346
+ executions.push({
2347
+ command,
2348
+ success,
2349
+ returncode: result.exitCode,
2350
+ output
2351
+ });
2352
+ } catch (error) {
2353
+ const isTimeout = error instanceof Error && error.message.includes("timed out");
2354
+ logger.error(
2355
+ `Hook command ${isTimeout ? "timed out" : "failed"} pane=${pane} command=${command}`
2356
+ );
2357
+ executions.push({
2358
+ command,
2359
+ success: false,
2360
+ returncode: isTimeout ? 124 : 1,
2361
+ output: isTimeout ? "Command timed out." : error instanceof Error ? error.message : "Unknown error"
2362
+ });
2363
+ }
2364
+ }
2365
+ return {
2366
+ pane,
2367
+ executions,
2368
+ hasFailures: executions.some((e) => !e.success)
2369
+ };
2370
+ }
2371
+ };
2372
+
2373
+ // ts-src/core/session.ts
2374
+ init_esm_shims();
2375
+ init_logger();
2376
+ var TERMINAL_MIN = 2;
2377
+ var TERMINAL_MAX = 16;
2378
+ function validateTerminalCount(value) {
2379
+ if (value < TERMINAL_MIN || value > TERMINAL_MAX) {
2380
+ throw new Error(`Invalid terminal count: ${value}`);
2381
+ }
2382
+ return value;
2383
+ }
2384
+ function parseRuntimeSnapshot(raw) {
2385
+ if (!isSessionSnapshot(raw)) {
2386
+ return null;
2387
+ }
2388
+ const snapshot = raw;
2389
+ if (snapshot.terminals.length === 0) {
2390
+ return null;
2391
+ }
2392
+ try {
2393
+ validateTerminalCount(snapshot.templateCount);
2394
+ } catch {
2395
+ return null;
2396
+ }
2397
+ const terminals = [];
2398
+ for (const item of snapshot.terminals) {
2399
+ if (!item.terminalId) {
2400
+ return null;
2401
+ }
2402
+ const runtime = normalizeRuntimeKind(item.runtime);
2403
+ if (!runtime) {
2404
+ return null;
2405
+ }
2406
+ terminals.push({
2407
+ terminalId: item.terminalId.trim(),
2408
+ title: item.title?.trim() || item.terminalId,
2409
+ runtime,
2410
+ repoPath: item.repoPath?.trim() || "",
2411
+ branch: item.branch?.trim() || ""
2412
+ });
2413
+ }
2414
+ return {
2415
+ layout: snapshot.layout || "grid",
2416
+ templateCount: snapshot.templateCount,
2417
+ focusedTerminalId: snapshot.focusedTerminalId?.trim() || "",
2418
+ terminals
2419
+ };
2420
+ }
2421
+ function normalizeRuntimeKind(value) {
2422
+ const normalized = value?.toLowerCase().trim();
2423
+ if (normalized === "wsl") return "wsl";
2424
+ if (normalized === "powershell") return "powershell";
2425
+ if (normalized === "native") return "native";
2426
+ return null;
2427
+ }
2428
+ var SessionCleanupHandler = class {
2429
+ manager;
2430
+ prompt;
2431
+ distribution;
2432
+ constructor(manager, prompt, distribution) {
2433
+ this.manager = manager;
2434
+ this.prompt = prompt;
2435
+ this.distribution = distribution;
2436
+ }
2437
+ async handleExit() {
2438
+ if (this.manager.getCleanupPolicy() === "persistent") {
2439
+ logger.debug("Session cleanup skipped due to persistent policy");
2440
+ return {
2441
+ closed: true,
2442
+ cancelled: false,
2443
+ removed: [],
2444
+ preservedDirty: this.manager.getManaged().map((w) => w.path)
2445
+ };
2446
+ }
2447
+ const dirty = [];
2448
+ const clean = [];
2449
+ for (const worktree of this.manager.getManaged()) {
2450
+ const isDirty = await this.manager.checkDirty(worktree, this.distribution);
2451
+ if (isDirty) {
2452
+ dirty.push(worktree);
2453
+ } else {
2454
+ clean.push(worktree);
2455
+ }
2456
+ }
2457
+ logger.debug(`Cleanup check complete dirty=${dirty.length} clean=${clean.length}`);
2458
+ if (dirty.length === 0) {
2459
+ const removed = await this.manager.cleanup({
2460
+ distribution: this.distribution
2461
+ });
2462
+ logger.debug(`All worktrees clean; removed=${removed.length}`);
2463
+ return {
2464
+ closed: true,
2465
+ cancelled: false,
2466
+ removed,
2467
+ preservedDirty: []
2468
+ };
2469
+ }
2470
+ const choice = await this.prompt(dirty.map((w) => w.path));
2471
+ logger.info(`Cleanup prompt result choice=${choice} dirty_count=${dirty.length}`);
2472
+ if (choice === "Vazgec" /* CANCEL */) {
2473
+ return {
2474
+ closed: false,
2475
+ cancelled: true,
2476
+ removed: [],
2477
+ preservedDirty: dirty.map((w) => w.path)
2478
+ };
2479
+ }
2480
+ if (choice === "Koruyarak Cik" /* PRESERVE */) {
2481
+ const removedClean = await this.manager.cleanup({
2482
+ selected: clean,
2483
+ distribution: this.distribution
2484
+ });
2485
+ logger.debug(`Preserve dirty worktrees; removed clean=${removedClean.length}`);
2486
+ return {
2487
+ closed: true,
2488
+ cancelled: false,
2489
+ removed: removedClean,
2490
+ preservedDirty: dirty.map((w) => w.path)
2491
+ };
2492
+ }
2493
+ const removedAll = await this.manager.cleanup({
2494
+ distribution: this.distribution
2495
+ });
2496
+ logger.debug(`Cleanup forced for all worktrees removed=${removedAll.length}`);
2497
+ return {
2498
+ closed: true,
2499
+ cancelled: false,
2500
+ removed: removedAll,
2501
+ preservedDirty: []
2502
+ };
2503
+ }
2504
+ };
2505
+
2506
+ // ts-src/commands/run.ts
2507
+ async function ensureTmuxInstalled() {
2508
+ const tmuxInstalled = await hasTmux();
2509
+ if (tmuxInstalled) {
2510
+ return;
2511
+ }
2512
+ const platform2 = detectPlatform();
2513
+ console.log(chalk9.yellow("\n\u{1F4E6} tmux bulunamad\u0131, kuruluyor...\n"));
2514
+ if (platform2 === "macos" /* MACOS */) {
2515
+ try {
2516
+ await execa("brew", ["install", "tmux"], { timeout: 12e4 });
2517
+ console.log(chalk9.green("\u2705 tmux kuruldu!\n"));
2518
+ return;
2519
+ } catch (error) {
2520
+ const msg = error instanceof Error ? error.message : String(error);
2521
+ throw new BranchNexusError(
2522
+ `tmux kurulumu ba\u015Far\u0131s\u0131z: ${msg}`,
2523
+ 6 /* TMUX_ERROR */,
2524
+ "Manuel kurulum: brew install tmux"
2525
+ );
2526
+ }
2527
+ }
2528
+ let installCmd;
2529
+ try {
2530
+ await execa("bash", ["-lc", "which apt-get"]);
2531
+ installCmd = "apt-get update && apt-get install -y tmux";
2532
+ } catch {
2533
+ try {
2534
+ await execa("bash", ["-lc", "which dnf"]);
2535
+ installCmd = "dnf install -y tmux";
2536
+ } catch {
2537
+ try {
2538
+ await execa("bash", ["-lc", "which pacman"]);
2539
+ installCmd = "pacman -S --noconfirm tmux";
2540
+ } catch {
2541
+ try {
2542
+ await execa("bash", ["-lc", "which apk"]);
2543
+ installCmd = "apk add tmux";
2544
+ } catch {
2545
+ throw new BranchNexusError(
2546
+ "Paket y\xF6neticisi bulunamad\u0131.",
2547
+ 6 /* TMUX_ERROR */,
2548
+ "L\xFCtfen tmux'u manuel kurun."
2549
+ );
2550
+ }
2551
+ }
2552
+ }
2553
+ }
2554
+ try {
2555
+ console.log(chalk9.dim(`\xC7al\u0131\u015Ft\u0131r\u0131l\u0131yor: sudo ${installCmd}`));
2556
+ await execa("sudo", ["-n", "bash", "-lc", installCmd], { timeout: 18e4 });
2557
+ console.log(chalk9.green("\u2705 tmux kuruldu!\n"));
2558
+ return;
2559
+ } catch {
2560
+ }
2561
+ console.log(chalk9.cyan("Ba\u015Fka bir terminalde bu komutu \xE7al\u0131\u015Ft\u0131r\u0131n:\n"));
2562
+ console.log(chalk9.bold.white(` sudo ${installCmd}
2563
+ `));
2564
+ console.log(chalk9.dim("tmux kurulumu bekleniyor..."));
2565
+ for (let i = 0; i < 60; i++) {
2566
+ await new Promise((r) => setTimeout(r, 2e3));
2567
+ const installed = await hasTmux();
2568
+ if (installed) {
2569
+ console.log(chalk9.green("\n\u2705 tmux alg\u0131land\u0131!\n"));
2570
+ return;
2571
+ }
2572
+ process.stdout.write(".");
2573
+ }
2574
+ console.log();
2575
+ throw new BranchNexusError(
2576
+ "2 dakika i\xE7inde tmux kurulumu alg\u0131lanamad\u0131.",
2577
+ 6 /* TMUX_ERROR */,
2578
+ "tmux kurduktan sonra tekrar deneyin."
2579
+ );
2580
+ }
2581
+ async function runCommand2(options) {
2582
+ if (options.logLevel !== void 0 && options.logLevel !== "" || options.logFile !== void 0 && options.logFile !== "") {
2583
+ configureLogging({
2584
+ level: options.logLevel,
2585
+ logFile: options.logFile
2586
+ });
2587
+ }
2588
+ logger.info("Starting BranchNexus");
2589
+ await ensureTmuxInstalled();
2590
+ const config = loadConfig();
2591
+ const sessionName = options.session ?? "branchnexus";
2592
+ if (options.fresh !== true && config.sessionRestoreEnabled && Object.keys(config.lastSession).length > 0) {
2593
+ const snapshot = parseRuntimeSnapshot(config.lastSession);
2594
+ if (snapshot !== null) {
2595
+ const restore = await p3.confirm({
2596
+ message: "Son oturumu geri y\xFCklemek ister misiniz?",
2597
+ initialValue: true
2598
+ });
2599
+ if (restore !== void 0 && restore !== null && restore === true) {
2600
+ logger.info("Restoring previous session");
2601
+ const platform3 = detectPlatform();
2602
+ const isWindows2 = platform3 === "windows" /* WINDOWS */;
2603
+ let distribution2 = "";
2604
+ if (isWindows2 && config.wslDistribution !== "") {
2605
+ distribution2 = config.wslDistribution;
2606
+ }
2607
+ const assignments2 = snapshot.terminals.map((t, i) => ({
2608
+ pane: i,
2609
+ repoPath: t.repoPath,
2610
+ branch: t.branch
2611
+ }));
2612
+ const worktreeBase2 = expandHomeDir(
2613
+ config.defaultRoot !== "" ? `${config.defaultRoot}/.bnx` : "~/.bnx"
2614
+ );
2615
+ const request2 = {
2616
+ distribution: distribution2,
2617
+ availableDistributions: [],
2618
+ layout: snapshot.layout,
2619
+ cleanupPolicy: config.cleanupPolicy,
2620
+ assignments: assignments2,
2621
+ worktreeBase: worktreeBase2,
2622
+ sessionName,
2623
+ tmuxAutoInstall: config.tmuxAutoInstall,
2624
+ colorTheme: config.colorTheme,
2625
+ paneNames: snapshot.terminals.map((t) => t.title),
2626
+ displayBranches: snapshot.terminals.map((t) => t.branch)
2627
+ };
2628
+ const s2 = p3.spinner();
2629
+ s2.start("Oturum geri y\xFCkleniyor...");
2630
+ try {
2631
+ const result = await orchestrate(request2);
2632
+ s2.stop(`${result.worktrees.length} worktree geri y\xFCklendi`);
2633
+ showSuccess("tmux session haz\u0131r! Ba\u011Flan\u0131l\u0131yor...");
2634
+ try {
2635
+ await execa("tmux", ["attach-session", "-t", sessionName], {
2636
+ stdio: "inherit",
2637
+ timeout: 0
2638
+ });
2639
+ } catch {
2640
+ console.log();
2641
+ console.log(chalk9.dim("tmux session devam ediyor. Tekrar ba\u011Flanmak i\xE7in:"));
2642
+ console.log(chalk9.cyan(` tmux attach -t ${sessionName}`));
2643
+ console.log();
2644
+ }
2645
+ return;
2646
+ } catch (error) {
2647
+ s2.stop("Geri y\xFCkleme ba\u015Far\u0131s\u0131z, yeni oturum ba\u015Flat\u0131l\u0131yor");
2648
+ logger.warn(`Session restore failed: ${error instanceof Error ? error.message : String(error)}`);
2649
+ }
2650
+ }
2651
+ }
2652
+ }
2653
+ const panelResult = await showPanel();
2654
+ if (!panelResult) {
2655
+ return;
2656
+ }
2657
+ const { token, repoUrl, repoUrls, layout, paneCount, cleanup } = panelResult;
2658
+ const allRepoUrls = repoUrls.length > 0 ? repoUrls : [repoUrl];
2659
+ const platform2 = detectPlatform();
2660
+ const isWindows = platform2 === "windows" /* WINDOWS */;
2661
+ let distribution = "";
2662
+ let availableDistributions = [];
2663
+ if (isWindows) {
2664
+ try {
2665
+ availableDistributions = await listDistributions();
2666
+ if (config.wslDistribution !== "" && availableDistributions.includes(config.wslDistribution)) {
2667
+ distribution = config.wslDistribution;
2668
+ } else {
2669
+ distribution = await promptWslDistribution();
2670
+ }
2671
+ } catch (error) {
2672
+ showError(
2673
+ "WSL distributions al\u0131namad\u0131",
2674
+ error instanceof Error ? error.message : String(error)
2675
+ );
2676
+ return;
2677
+ }
2678
+ }
2679
+ const workspaceRoot = expandHomeDir(
2680
+ config.defaultRoot !== "" ? config.defaultRoot : "~/workspace"
2681
+ );
2682
+ if (!existsSync(workspaceRoot)) {
2683
+ mkdirSync(workspaceRoot, { recursive: true });
2684
+ }
2685
+ const s = p3.spinner();
2686
+ const localRepoPaths = [];
2687
+ for (const url of allRepoUrls) {
2688
+ let rName = basename(url.replace(".git", ""));
2689
+ if (rName.startsWith("git@")) {
2690
+ rName = rName.split(":").pop()?.replace(".git", "") ?? "repo";
2691
+ }
2692
+ const localPath = join(workspaceRoot, rName);
2693
+ localRepoPaths.push(localPath);
2694
+ if (existsSync(localPath) && existsSync(join(localPath, ".git"))) {
2695
+ s.start(`Mevcut repo kullan\u0131l\u0131yor: ${rName}...`);
2696
+ try {
2697
+ const git = simpleGit2(localPath);
2698
+ await git.fetch("origin");
2699
+ s.stop("Mevcut repo: " + localPath);
2700
+ } catch {
2701
+ s.stop("Fetch ba\u015Far\u0131s\u0131z, local ile devam");
2702
+ }
2703
+ } else {
2704
+ s.start(`Repo klonlan\u0131yor: ${rName}...`);
2705
+ let cloneUrl = url;
2706
+ if (token !== null && token !== "" && url.startsWith("https://")) {
2707
+ cloneUrl = url.replace("https://", `https://${token}@`);
2708
+ }
2709
+ try {
2710
+ await cloneRepository(cloneUrl, localPath);
2711
+ s.stop("Klonland\u0131: " + localPath);
2712
+ } catch (error) {
2713
+ s.stop("Klonlama ba\u015Far\u0131s\u0131z");
2714
+ const message = error instanceof Error ? error.message : String(error);
2715
+ if (message.includes("403") || message.includes("Authentication failed")) {
2716
+ showError("Kimlik do\u011Frulama ba\u015Far\u0131s\u0131z", "Private repo i\xE7in GitHub token gerekli");
2717
+ return;
2718
+ }
2719
+ showError("Repo klonlanamad\u0131", message);
2720
+ return;
2721
+ }
2722
+ }
2723
+ }
2724
+ const localRepoPath = localRepoPaths[0];
2725
+ const allBranches = [];
2726
+ const branchRepoMap = /* @__PURE__ */ new Map();
2727
+ try {
2728
+ const { listLocalBranches: listLocalBranches2 } = await Promise.resolve().then(() => (init_branch(), branch_exports));
2729
+ for (let ri = 0; ri < localRepoPaths.length; ri++) {
2730
+ const rPath = localRepoPaths[ri];
2731
+ const branchResult = await listLocalBranches2(rPath);
2732
+ if (branchResult.warning !== void 0 && branchResult.warning !== "") {
2733
+ p3.log.warn(branchResult.warning);
2734
+ }
2735
+ for (const branch of branchResult.branches) {
2736
+ const key = localRepoPaths.length > 1 ? `[${basename(rPath)}] ${branch}` : branch;
2737
+ allBranches.push(key);
2738
+ branchRepoMap.set(key, rPath);
2739
+ }
2740
+ }
2741
+ if (allBranches.length === 0) {
2742
+ showError("Repository'de branch bulunamad\u0131.");
2743
+ return;
2744
+ }
2745
+ } catch (error) {
2746
+ showError("Branch listesi al\u0131namad\u0131", error instanceof Error ? error.message : String(error));
2747
+ return;
2748
+ }
2749
+ let branches;
2750
+ let paneNames = [];
2751
+ let startupCommands = [];
2752
+ try {
2753
+ const selected = await showBranchSelection(allBranches, paneCount);
2754
+ if (selected === null) {
2755
+ p3.cancel("\u0130ptal edildi.");
2756
+ return;
2757
+ }
2758
+ branches = selected.branches;
2759
+ paneNames = selected.paneNames;
2760
+ startupCommands = selected.startupCommands ?? [];
2761
+ } catch (error) {
2762
+ showError("Branch se\xE7imi ba\u015Far\u0131s\u0131z", error instanceof Error ? error.message : String(error));
2763
+ return;
2764
+ }
2765
+ showPreview({
2766
+ repoUrl,
2767
+ layout,
2768
+ branchCount: branches.length,
2769
+ branches,
2770
+ cleanup
2771
+ });
2772
+ const confirmed = await confirmStart();
2773
+ if (!confirmed) {
2774
+ p3.cancel("\u0130ptal edildi.");
2775
+ return;
2776
+ }
2777
+ s.start("Worktree'ler olu\u015Fturuluyor...");
2778
+ const resolvedBranches = [];
2779
+ const paneRepoPaths = [];
2780
+ for (let i = 0; i < branches.length; i++) {
2781
+ const branchKey = branches[i];
2782
+ const actualBranch = branchKey.replace(/^\[.*?\]\s*/, "");
2783
+ const paneRepoPath = branchRepoMap.get(branchKey) ?? localRepoPath;
2784
+ paneRepoPaths.push(paneRepoPath);
2785
+ const git = simpleGit2(paneRepoPath);
2786
+ const safeName = paneNames[i].replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || `pane-${i}`;
2787
+ const localName = `bnx-${safeName}-${i}`;
2788
+ try {
2789
+ try {
2790
+ await git.branch(["-D", localName]);
2791
+ } catch {
2792
+ }
2793
+ await git.branch([localName, actualBranch]);
2794
+ logger.debug(`Created local branch ${localName} from ${actualBranch}`);
2795
+ } catch {
2796
+ logger.debug(`Branch ${localName} already exists, reusing`);
2797
+ }
2798
+ resolvedBranches.push(localName);
2799
+ }
2800
+ const assignments = resolvedBranches.map((branch, index) => ({
2801
+ pane: index,
2802
+ repoPath: paneRepoPaths[index],
2803
+ branch
2804
+ }));
2805
+ const worktreeBase = expandHomeDir(
2806
+ config.defaultRoot !== "" ? `${config.defaultRoot}/.bnx` : "~/.bnx"
2807
+ );
2808
+ const request = {
2809
+ distribution,
2810
+ availableDistributions,
2811
+ layout,
2812
+ cleanupPolicy: cleanup,
2813
+ assignments,
2814
+ worktreeBase,
2815
+ sessionName,
2816
+ tmuxAutoInstall: config.tmuxAutoInstall,
2817
+ colorTheme: config.colorTheme,
2818
+ paneNames,
2819
+ displayBranches: branches,
2820
+ startupCommands: startupCommands.length > 0 ? startupCommands : void 0
2821
+ };
2822
+ try {
2823
+ const result = await orchestrate(request);
2824
+ s.stop(`${result.worktrees.length} worktree olu\u015Fturuldu`);
2825
+ const noHooks = options.hooks === false;
2826
+ const hookCommands = config.commandHooks["post-setup"] ?? [];
2827
+ if (!noHooks && hookCommands.length > 0) {
2828
+ const hookSpinner = p3.spinner();
2829
+ hookSpinner.start("Hook'lar \xE7al\u0131\u015Ft\u0131r\u0131l\u0131yor...");
2830
+ const runner = new HookRunner({ timeoutSeconds: 60 });
2831
+ let totalFailures = 0;
2832
+ for (let i = 0; i < result.worktrees.length; i++) {
2833
+ const hookResult = await runner.run(i, hookCommands, distribution || void 0);
2834
+ if (hookResult.hasFailures) {
2835
+ totalFailures += hookResult.executions.filter((e) => !e.success).length;
2836
+ }
2837
+ }
2838
+ if (totalFailures > 0) {
2839
+ hookSpinner.stop(chalk9.yellow(`Hook'lar tamamland\u0131 (${totalFailures} hata)`));
2840
+ } else {
2841
+ hookSpinner.stop("Hook'lar tamamland\u0131");
2842
+ }
2843
+ }
2844
+ const snapshotData = {
2845
+ layout,
2846
+ templateCount: branches.length,
2847
+ focusedTerminalId: "",
2848
+ terminals: branches.map((branch, i) => ({
2849
+ terminalId: `pane-${i}`,
2850
+ title: paneNames[i] ?? `Pane ${i}`,
2851
+ runtime: "native",
2852
+ repoPath: localRepoPath,
2853
+ branch
2854
+ }))
2855
+ };
2856
+ updateLastSession(snapshotData);
2857
+ showSuccess("tmux session haz\u0131r! Ba\u011Flan\u0131l\u0131yor...");
2858
+ try {
2859
+ await execa("tmux", ["attach-session", "-t", sessionName], {
2860
+ stdio: "inherit",
2861
+ timeout: 0
2862
+ });
2863
+ } catch {
2864
+ console.log();
2865
+ console.log(chalk9.dim("tmux session devam ediyor. Tekrar ba\u011Flanmak i\xE7in:"));
2866
+ console.log(chalk9.cyan(` tmux attach -t ${sessionName}`));
2867
+ console.log();
2868
+ }
2869
+ if (cleanup === "session" && result.worktrees.length > 0) {
2870
+ const worktreeManager = new WorktreeManager(worktreeBase, cleanup);
2871
+ for (const wt of result.worktrees) {
2872
+ await worktreeManager.addWorktree(
2873
+ { pane: wt.pane, repoPath: wt.repoPath, branch: wt.branch },
2874
+ distribution || void 0
2875
+ );
2876
+ }
2877
+ const cleanupHandler = new SessionCleanupHandler(
2878
+ worktreeManager,
2879
+ promptCleanup,
2880
+ distribution || void 0
2881
+ );
2882
+ const cleanupResult = await cleanupHandler.handleExit();
2883
+ if (cleanupResult.removed.length > 0) {
2884
+ console.log(chalk9.green(`\u2713 ${cleanupResult.removed.length} worktree temizlendi.`));
2885
+ }
2886
+ if (cleanupResult.preservedDirty.length > 0) {
2887
+ console.log(
2888
+ chalk9.yellow(`\u26A0 ${cleanupResult.preservedDirty.length} dirty worktree korundu.`)
2889
+ );
2890
+ }
2891
+ }
2892
+ } catch (error) {
2893
+ s.stop("Session olu\u015Fturulamad\u0131");
2894
+ if (error instanceof BranchNexusError) {
2895
+ showError(error.message, error.hint);
2896
+ } else {
2897
+ showError("Beklenmeyen hata", error instanceof Error ? error.message : String(error));
2898
+ }
2899
+ }
2900
+ }
2901
+
2902
+ // ts-src/commands/config.ts
2903
+ init_esm_shims();
2904
+ function configCommand(action, key, value) {
2905
+ const config = loadConfig();
2906
+ switch (action) {
2907
+ case "show":
2908
+ console.log(chalk9.bold("\n\u{1F4CB} BranchNexus Configuration\n"));
2909
+ console.log(chalk9.dim(`Location: ${getConfigPath()}
2910
+ `));
2911
+ console.log(JSON.stringify(config, null, 2));
2912
+ console.log();
2913
+ break;
2914
+ case "set":
2915
+ if (key === void 0 || key === "" || value === void 0) {
2916
+ console.error(chalk9.red("Usage: branchnexus config set <key> <value>"));
2917
+ console.log(chalk9.dim("\nAvailable keys:"));
2918
+ console.log(" defaultRoot, defaultLayout, defaultPanes, cleanupPolicy");
2919
+ console.log(" wslDistribution, tmuxAutoInstall, sessionRestoreEnabled");
2920
+ console.log(" githubToken, terminalMaxCount, colorTheme");
2921
+ process.exit(1);
2922
+ }
2923
+ try {
2924
+ setConfigValue(key, value);
2925
+ console.log(chalk9.green(`
2926
+ \u2705 Set ${key} = ${value}
2927
+ `));
2928
+ } catch (error) {
2929
+ const message = error instanceof Error ? error.message : String(error);
2930
+ console.error(chalk9.red(`
2931
+ \u274C ${message}
2932
+ `));
2933
+ process.exit(1);
2934
+ }
2935
+ break;
2936
+ case "reset":
2937
+ resetConfig();
2938
+ console.log(chalk9.green("\n\u2705 Configuration reset to defaults\n"));
2939
+ break;
2940
+ case "export": {
2941
+ const exportData = JSON.stringify(config, null, 2);
2942
+ console.log(exportData);
2943
+ break;
2944
+ }
2945
+ case "import": {
2946
+ if (key === void 0 || key === "") {
2947
+ console.error(chalk9.red("Usage: branchnexus config import <file-path>"));
2948
+ process.exit(1);
2949
+ }
2950
+ try {
2951
+ const fileContent = readFileSync(key, "utf-8");
2952
+ const parsed = JSON.parse(fileContent);
2953
+ const validated = AppConfigSchema.parse(parsed);
2954
+ saveConfig(validated);
2955
+ console.log(chalk9.green(`
2956
+ \u2705 Configuration imported from ${key}
2957
+ `));
2958
+ } catch (error) {
2959
+ const message = error instanceof Error ? error.message : String(error);
2960
+ console.error(chalk9.red(`
2961
+ \u274C Import ba\u015Far\u0131s\u0131z: ${message}
2962
+ `));
2963
+ process.exit(1);
2964
+ }
2965
+ break;
2966
+ }
2967
+ default:
2968
+ console.log(chalk9.bold("\n\u{1F4CB} Configuration Commands\n"));
2969
+ console.log(" branchnexus config show Show current configuration");
2970
+ console.log(" branchnexus config set <key> <value> Set a configuration value");
2971
+ console.log(" branchnexus config reset Reset to defaults");
2972
+ console.log(" branchnexus config export Export config as JSON");
2973
+ console.log(" branchnexus config import <file> Import config from JSON file");
2974
+ console.log();
2975
+ }
2976
+ }
2977
+
2978
+ // ts-src/commands/kill.ts
2979
+ init_esm_shims();
2980
+ init_logger();
2981
+ var SESSION_PREFIX = "branchnexus";
2982
+ async function cleanupWorktreeDir(basePath, distribution) {
2983
+ if (!existsSync(basePath)) {
2984
+ return 0;
2985
+ }
2986
+ let removed = 0;
2987
+ const repoDirs = readdirSync(basePath, { withFileTypes: true }).filter((d) => d.isDirectory());
2988
+ for (const repoDir of repoDirs) {
2989
+ const repoWorktreeDir = join(basePath, repoDir.name);
2990
+ const paneDirs = readdirSync(repoWorktreeDir, { withFileTypes: true }).filter((d) => d.isDirectory());
2991
+ for (const paneDir of paneDirs) {
2992
+ const panePath = join(repoWorktreeDir, paneDir.name);
2993
+ const gitFile = join(panePath, ".git");
2994
+ if (!existsSync(gitFile)) {
2995
+ continue;
2996
+ }
2997
+ const cmd = ["git", "-C", panePath, "worktree", "list", "--porcelain"];
2998
+ try {
2999
+ const result = distribution ? await runCommand(["wsl", "-d", distribution, ...cmd]) : await runCommand(cmd);
3000
+ let mainRepoPath = "";
3001
+ for (const line of result.stdout.split("\n")) {
3002
+ if (line.startsWith("worktree ")) {
3003
+ mainRepoPath = line.slice(9).trim();
3004
+ break;
3005
+ }
3006
+ }
3007
+ if (mainRepoPath !== "") {
3008
+ const removeCmd = ["git", "-C", mainRepoPath, "worktree", "remove", "--force", panePath];
3009
+ const removeResult = distribution ? await runCommand(["wsl", "-d", distribution, ...removeCmd]) : await runCommand(removeCmd);
3010
+ if (removeResult.exitCode === 0) {
3011
+ removed++;
3012
+ logger.debug(`Removed worktree: ${panePath}`);
3013
+ } else {
3014
+ logger.warn(`Failed to remove worktree ${panePath}: ${removeResult.stderr}`);
3015
+ }
3016
+ }
3017
+ } catch (error) {
3018
+ logger.warn(`Error cleaning worktree ${panePath}: ${error instanceof Error ? error.message : String(error)}`);
3019
+ }
3020
+ }
3021
+ try {
3022
+ const remaining = readdirSync(repoWorktreeDir);
3023
+ if (remaining.length === 0) {
3024
+ const { rmSync } = await import('fs');
3025
+ rmSync(repoWorktreeDir, { recursive: true });
3026
+ logger.debug(`Removed empty worktree dir: ${repoWorktreeDir}`);
3027
+ }
3028
+ } catch {
3029
+ }
3030
+ }
3031
+ try {
3032
+ const remaining = readdirSync(basePath);
3033
+ if (remaining.length === 0) {
3034
+ const { rmSync } = await import('fs');
3035
+ rmSync(basePath, { recursive: true });
3036
+ logger.debug(`Removed empty worktree base: ${basePath}`);
3037
+ }
3038
+ } catch {
3039
+ }
3040
+ return removed;
3041
+ }
3042
+ async function cleanupWorktrees(distribution) {
3043
+ const config = loadConfig();
3044
+ const root = config.defaultRoot !== "" ? config.defaultRoot : expandHomeDir("~");
3045
+ const workspaceRoot = config.defaultRoot !== "" ? config.defaultRoot : expandHomeDir("~/workspace");
3046
+ const paths = [
3047
+ join(root, ".bnx"),
3048
+ join(workspaceRoot, ".bnx"),
3049
+ join(workspaceRoot, "branchnexus-worktrees"),
3050
+ join(root, "branchnexus-worktrees")
3051
+ ];
3052
+ let total = 0;
3053
+ const seen = /* @__PURE__ */ new Set();
3054
+ for (const p6 of paths) {
3055
+ if (seen.has(p6)) continue;
3056
+ seen.add(p6);
3057
+ total += await cleanupWorktreeDir(p6, distribution);
3058
+ }
3059
+ return total;
3060
+ }
3061
+ async function killCommand(sessionName) {
3062
+ const config = loadConfig();
3063
+ const distribution = config.wslDistribution || void 0;
3064
+ const allSessions = await listSessions(distribution);
3065
+ const bnSessions = allSessions.filter((s) => s.startsWith(SESSION_PREFIX));
3066
+ if (bnSessions.length === 0) {
3067
+ console.log(chalk9.yellow("\nAktif BranchNexus session bulunamad\u0131.\n"));
3068
+ const removed2 = await cleanupWorktrees(distribution);
3069
+ if (removed2 > 0) {
3070
+ console.log(chalk9.green(`${removed2} orphan worktree temizlendi.
3071
+ `));
3072
+ }
3073
+ return;
3074
+ }
3075
+ let target;
3076
+ if (sessionName !== void 0 && sessionName !== "") {
3077
+ if (!bnSessions.includes(sessionName)) {
3078
+ console.error(chalk9.red(`
3079
+ Session "${sessionName}" bulunamad\u0131.
3080
+ `));
3081
+ console.log(chalk9.dim("Aktif session'lar:"));
3082
+ for (const s of bnSessions) {
3083
+ console.log(chalk9.dim(` - ${s}`));
3084
+ }
3085
+ console.log();
3086
+ process.exit(1);
3087
+ }
3088
+ target = sessionName;
3089
+ } else if (bnSessions.length === 1) {
3090
+ target = bnSessions[0];
3091
+ } else {
3092
+ const selected = await p3.select({
3093
+ message: "Hangi session kapat\u0131ls\u0131n?",
3094
+ options: bnSessions.map((s) => ({ value: s, label: s }))
3095
+ });
3096
+ if (p3.isCancel(selected)) {
3097
+ p3.cancel("\u0130ptal edildi.");
3098
+ return;
3099
+ }
3100
+ target = selected;
3101
+ }
3102
+ await killSession(target, distribution);
3103
+ console.log(chalk9.green(`
3104
+ \u2713 Session "${target}" kapat\u0131ld\u0131.`));
3105
+ const removed = await cleanupWorktrees(distribution);
3106
+ if (removed > 0) {
3107
+ console.log(chalk9.green(`\u2713 ${removed} worktree temizlendi.
3108
+ `));
3109
+ } else {
3110
+ console.log();
3111
+ }
3112
+ }
3113
+
3114
+ // ts-src/commands/preset.ts
3115
+ init_esm_shims();
3116
+ function presetCommand(action, name, extra) {
3117
+ switch (action) {
3118
+ case "list": {
3119
+ const presets = loadPresets();
3120
+ const entries = Object.entries(presets);
3121
+ if (entries.length === 0) {
3122
+ console.log(chalk9.yellow("\nKay\u0131tl\u0131 preset bulunamad\u0131.\n"));
3123
+ console.log(chalk9.dim("Yeni preset: branchnexus preset save <isim>"));
3124
+ console.log();
3125
+ return;
3126
+ }
3127
+ console.log(chalk9.bold("\n\u{1F4CB} Preset Listesi\n"));
3128
+ for (const [presetName, preset] of entries) {
3129
+ const p6 = preset;
3130
+ console.log(
3131
+ chalk9.cyan(` ${presetName}`) + chalk9.dim(` \u2014 layout: ${p6.layout}, panes: ${p6.panes}, cleanup: ${p6.cleanup}`)
3132
+ );
3133
+ }
3134
+ console.log();
3135
+ break;
3136
+ }
3137
+ case "save": {
3138
+ if (name === void 0 || name === "") {
3139
+ console.error(chalk9.red("Kullan\u0131m: branchnexus preset save <isim>"));
3140
+ process.exit(1);
3141
+ }
3142
+ if (extra !== void 0 && extra !== "") {
3143
+ try {
3144
+ const parsed = JSON.parse(extra);
3145
+ savePreset(name, parsed);
3146
+ console.log(chalk9.green(`
3147
+ \u2713 Preset "${name}" kaydedildi.
3148
+ `));
3149
+ } catch (error) {
3150
+ const msg = error instanceof Error ? error.message : String(error);
3151
+ console.error(chalk9.red(`
3152
+ Ge\xE7ersiz preset verisi: ${msg}
3153
+ `));
3154
+ process.exit(1);
3155
+ }
3156
+ } else {
3157
+ const preset = createPresetFromCurrentConfig(name);
3158
+ console.log(chalk9.green(`
3159
+ \u2713 Preset "${name}" mevcut ayarlardan olu\u015Fturuldu.`));
3160
+ console.log(
3161
+ chalk9.dim(` layout: ${preset.layout}, panes: ${preset.panes}, cleanup: ${preset.cleanup}`)
3162
+ );
3163
+ console.log();
3164
+ }
3165
+ break;
3166
+ }
3167
+ case "load": {
3168
+ if (name === void 0 || name === "") {
3169
+ console.error(chalk9.red("Kullan\u0131m: branchnexus preset load <isim>"));
3170
+ process.exit(1);
3171
+ }
3172
+ try {
3173
+ const preset = applyPreset(name);
3174
+ console.log(chalk9.green(`
3175
+ \u2713 Preset "${name}" y\xFCklendi.`));
3176
+ console.log(
3177
+ chalk9.dim(` layout: ${preset.layout}, panes: ${preset.panes}, cleanup: ${preset.cleanup}`)
3178
+ );
3179
+ console.log();
3180
+ } catch (error) {
3181
+ const msg = error instanceof Error ? error.message : String(error);
3182
+ console.error(chalk9.red(`
3183
+ ${msg}
3184
+ `));
3185
+ process.exit(1);
3186
+ }
3187
+ break;
3188
+ }
3189
+ case "delete": {
3190
+ if (name === void 0 || name === "") {
3191
+ console.error(chalk9.red("Kullan\u0131m: branchnexus preset delete <isim>"));
3192
+ process.exit(1);
3193
+ }
3194
+ if (!presetExists(name)) {
3195
+ console.error(chalk9.red(`
3196
+ Preset "${name}" bulunamad\u0131.
3197
+ `));
3198
+ process.exit(1);
3199
+ }
3200
+ deletePreset(name);
3201
+ console.log(chalk9.green(`
3202
+ \u2713 Preset "${name}" silindi.
3203
+ `));
3204
+ break;
3205
+ }
3206
+ case "rename": {
3207
+ if (name === void 0 || name === "" || extra === void 0 || extra === "") {
3208
+ console.error(chalk9.red("Kullan\u0131m: branchnexus preset rename <eski-isim> <yeni-isim>"));
3209
+ process.exit(1);
3210
+ }
3211
+ try {
3212
+ renamePreset(name, extra);
3213
+ console.log(chalk9.green(`
3214
+ \u2713 Preset "${name}" \u2192 "${extra}" olarak yeniden adland\u0131r\u0131ld\u0131.
3215
+ `));
3216
+ } catch (error) {
3217
+ const msg = error instanceof Error ? error.message : String(error);
3218
+ console.error(chalk9.red(`
3219
+ ${msg}
3220
+ `));
3221
+ process.exit(1);
3222
+ }
3223
+ break;
3224
+ }
3225
+ default:
3226
+ console.log(chalk9.bold("\n\u{1F4CB} Preset Komutlar\u0131\n"));
3227
+ console.log(" branchnexus preset list Kay\u0131tl\u0131 preset'leri listele");
3228
+ console.log(" branchnexus preset save <isim> Mevcut ayarlardan preset kaydet");
3229
+ console.log(" branchnexus preset load <isim> Preset y\xFCkle");
3230
+ console.log(" branchnexus preset delete <isim> Preset sil");
3231
+ console.log(" branchnexus preset rename <eski> <yeni> Preset yeniden adland\u0131r");
3232
+ console.log();
3233
+ }
3234
+ }
3235
+
3236
+ // ts-src/commands/status.ts
3237
+ init_esm_shims();
3238
+ async function statusCommand() {
3239
+ const config = loadConfig();
3240
+ const distribution = config.wslDistribution || void 0;
3241
+ console.log(chalk9.bold("\n\u{1F4CA} BranchNexus Durum\n"));
3242
+ console.log(chalk9.dim("Config: ") + getConfigPath());
3243
+ console.log();
3244
+ console.log(chalk9.bold("tmux Session'lar:"));
3245
+ const sessions = await listSessions(distribution);
3246
+ const bnSessions = sessions.filter((s) => s.startsWith("branchnexus"));
3247
+ if (bnSessions.length === 0) {
3248
+ console.log(chalk9.dim(" Aktif session yok"));
3249
+ } else {
3250
+ for (const s of bnSessions) {
3251
+ console.log(chalk9.green(` \u25CF ${s}`));
3252
+ }
3253
+ }
3254
+ console.log();
3255
+ console.log(chalk9.bold("Worktree'ler:"));
3256
+ const root = config.defaultRoot !== "" ? config.defaultRoot : expandHomeDir("~");
3257
+ const bnxDir = join(root, ".bnx");
3258
+ if (existsSync(bnxDir)) {
3259
+ try {
3260
+ const repoDirs = readdirSync(bnxDir, { withFileTypes: true }).filter((d) => d.isDirectory());
3261
+ let wtCount = 0;
3262
+ for (const repoDir of repoDirs) {
3263
+ const repoPath = join(bnxDir, repoDir.name);
3264
+ const paneDirs = readdirSync(repoPath, { withFileTypes: true }).filter(
3265
+ (d) => d.isDirectory()
3266
+ );
3267
+ for (const paneDir of paneDirs) {
3268
+ const gitFile = join(repoPath, paneDir.name, ".git");
3269
+ if (existsSync(gitFile)) {
3270
+ wtCount++;
3271
+ console.log(chalk9.cyan(` ${repoDir.name}/${paneDir.name}`));
3272
+ }
3273
+ }
3274
+ }
3275
+ if (wtCount === 0) {
3276
+ console.log(chalk9.dim(" Aktif worktree yok"));
3277
+ }
3278
+ } catch {
3279
+ console.log(chalk9.dim(" Worktree dizini okunamad\u0131"));
3280
+ }
3281
+ } else {
3282
+ console.log(chalk9.dim(" Aktif worktree yok"));
3283
+ }
3284
+ console.log();
3285
+ console.log(chalk9.bold("Preset'ler:"));
3286
+ const presets = loadPresets();
3287
+ const presetEntries = Object.entries(presets);
3288
+ if (presetEntries.length === 0) {
3289
+ console.log(chalk9.dim(" Kay\u0131tl\u0131 preset yok"));
3290
+ } else {
3291
+ for (const [name, preset] of presetEntries) {
3292
+ console.log(
3293
+ chalk9.cyan(` ${name}`) + chalk9.dim(` \u2014 ${preset.layout}, ${preset.panes} pane, ${preset.cleanup}`)
3294
+ );
3295
+ }
3296
+ }
3297
+ console.log();
3298
+ console.log(chalk9.bold("Hook'lar:"));
3299
+ const hooks = config.commandHooks;
3300
+ const hookEntries = Object.entries(hooks);
3301
+ if (hookEntries.length === 0) {
3302
+ console.log(chalk9.dim(" Tan\u0131ml\u0131 hook yok"));
3303
+ } else {
3304
+ for (const [event, commands] of hookEntries) {
3305
+ console.log(chalk9.cyan(` ${event}:`));
3306
+ for (const cmd of commands) {
3307
+ console.log(chalk9.dim(` $ ${cmd}`));
3308
+ }
3309
+ }
3310
+ }
3311
+ console.log();
3312
+ console.log(chalk9.bold("GitHub Token:"));
3313
+ const hasToken = config.githubToken !== "";
3314
+ const envToken = process.env.BRANCHNEXUS_GH_TOKEN;
3315
+ const tokenSource = envToken !== void 0 && envToken !== "" ? " (env)" : " (config)";
3316
+ console.log(
3317
+ hasToken ? chalk9.green(` \u25CF Tan\u0131ml\u0131${tokenSource}`) : chalk9.dim(" \u25CB Tan\u0131ml\u0131 de\u011Fil")
3318
+ );
3319
+ console.log();
3320
+ console.log(chalk9.bold("Ayarlar:"));
3321
+ console.log(chalk9.dim(" Layout: ") + config.defaultLayout);
3322
+ console.log(chalk9.dim(" Panes: ") + config.defaultPanes);
3323
+ console.log(chalk9.dim(" Cleanup: ") + config.cleanupPolicy);
3324
+ console.log(chalk9.dim(" Theme: ") + config.colorTheme);
3325
+ console.log(chalk9.dim(" Restore: ") + (config.sessionRestoreEnabled ? "A\xE7\u0131k" : "Kapal\u0131"));
3326
+ if (config.wslDistribution !== "") {
3327
+ console.log(chalk9.dim(" WSL: ") + config.wslDistribution);
3328
+ }
3329
+ console.log();
3330
+ }
3331
+
3332
+ // ts-src/commands/attach.ts
3333
+ init_esm_shims();
3334
+ var SESSION_PREFIX2 = "branchnexus";
3335
+ async function attachCommand(sessionName) {
3336
+ const config = loadConfig();
3337
+ const distribution = config.wslDistribution || void 0;
3338
+ const allSessions = await listSessions(distribution);
3339
+ const bnSessions = allSessions.filter((s) => s.startsWith(SESSION_PREFIX2));
3340
+ if (bnSessions.length === 0) {
3341
+ console.log(chalk9.yellow("\nAktif BranchNexus session bulunamad\u0131.\n"));
3342
+ console.log(chalk9.dim("Yeni session ba\u015Flatmak i\xE7in: branchnexus"));
3343
+ console.log();
3344
+ return;
3345
+ }
3346
+ let target;
3347
+ if (sessionName !== void 0 && sessionName !== "") {
3348
+ if (!bnSessions.includes(sessionName)) {
3349
+ console.error(chalk9.red(`
3350
+ Session "${sessionName}" bulunamad\u0131.
3351
+ `));
3352
+ console.log(chalk9.dim("Aktif session'lar:"));
3353
+ for (const s of bnSessions) {
3354
+ console.log(chalk9.dim(` - ${s}`));
3355
+ }
3356
+ console.log();
3357
+ process.exit(1);
3358
+ }
3359
+ target = sessionName;
3360
+ } else if (bnSessions.length === 1) {
3361
+ target = bnSessions[0];
3362
+ } else {
3363
+ const selected = await p3.select({
3364
+ message: "Hangi session'a ba\u011Flan\u0131ls\u0131n?",
3365
+ options: bnSessions.map((s) => ({ value: s, label: s }))
3366
+ });
3367
+ if (p3.isCancel(selected)) {
3368
+ p3.cancel("\u0130ptal edildi.");
3369
+ return;
3370
+ }
3371
+ target = selected;
3372
+ }
3373
+ console.log(chalk9.cyan(`
3374
+ Session "${target}" ba\u011Flan\u0131l\u0131yor...
3375
+ `));
3376
+ try {
3377
+ await execa("tmux", ["attach-session", "-t", target], {
3378
+ stdio: "inherit",
3379
+ timeout: 0
3380
+ });
3381
+ } catch {
3382
+ console.log();
3383
+ console.log(chalk9.dim("tmux session devam ediyor. Tekrar ba\u011Flanmak i\xE7in:"));
3384
+ console.log(chalk9.cyan(` branchnexus attach ${target}`));
3385
+ console.log();
3386
+ }
3387
+ }
3388
+
3389
+ // ts-src/prompts/setup.ts
3390
+ init_esm_shims();
3391
+ init_logger();
3392
+ async function checkDependencies() {
3393
+ const tmuxInstalled = await hasTmux();
3394
+ let gitInstalled = false;
3395
+ try {
3396
+ await execa("git", ["--version"]);
3397
+ gitInstalled = true;
3398
+ } catch {
3399
+ gitInstalled = false;
3400
+ }
3401
+ return { tmux: tmuxInstalled, git: gitInstalled };
3402
+ }
3403
+ async function installTmux() {
3404
+ const platform2 = detectPlatform();
3405
+ console.log(chalk9.dim("\n\u{1F4E6} Installing tmux...\n"));
3406
+ let cmd;
3407
+ if (platform2 === "macos" /* MACOS */) {
3408
+ cmd = "brew install tmux";
3409
+ } else {
3410
+ try {
3411
+ await execa("bash", ["-lc", "which apt-get"]);
3412
+ cmd = "sudo apt-get update && sudo apt-get install -y tmux";
3413
+ } catch {
3414
+ try {
3415
+ await execa("bash", ["-lc", "which dnf"]);
3416
+ cmd = "sudo dnf install -y tmux";
3417
+ } catch {
3418
+ try {
3419
+ await execa("bash", ["-lc", "which pacman"]);
3420
+ cmd = "sudo pacman -S --noconfirm tmux";
3421
+ } catch {
3422
+ console.log(chalk9.red("Could not detect package manager. Please install tmux manually."));
3423
+ return false;
3424
+ }
3425
+ }
3426
+ }
3427
+ }
3428
+ console.log(chalk9.dim(`Running: ${cmd}`));
3429
+ try {
3430
+ if (cmd.includes("sudo")) {
3431
+ console.log(chalk9.yellow("\n\u26A0\uFE0F sudo password required. Running in your shell...\n"));
3432
+ console.log(chalk9.cyan(`Please run: ${cmd}
3433
+ `));
3434
+ const { proceed } = await inquirer2.prompt([
3435
+ {
3436
+ type: "confirm",
3437
+ name: "proceed",
3438
+ message: "Press Enter after you have run the command above",
3439
+ default: true
3440
+ }
3441
+ ]);
3442
+ return proceed;
3443
+ } else {
3444
+ await execa("bash", ["-lc", cmd]);
3445
+ console.log(chalk9.green("\u2705 tmux installed successfully!\n"));
3446
+ return true;
3447
+ }
3448
+ } catch (error) {
3449
+ const message = error instanceof Error ? error.message : String(error);
3450
+ console.log(chalk9.red(`Failed to install tmux: ${message}`));
3451
+ console.log(chalk9.dim(`Please run manually: ${cmd}`));
3452
+ return false;
3453
+ }
3454
+ }
3455
+ async function promptSetup() {
3456
+ console.log(chalk9.cyan.bold("\n\u{1F680} BranchNexus Setup Wizard\n"));
3457
+ const platform2 = detectPlatform();
3458
+ const currentConfig = loadConfig();
3459
+ const deps = await checkDependencies();
3460
+ if (!deps.git) {
3461
+ console.log(chalk9.red("\u274C Git is not installed. Please install Git first."));
3462
+ console.log(chalk9.dim("https://git-scm.com/downloads\n"));
3463
+ process.exit(1);
3464
+ }
3465
+ if (!deps.tmux) {
3466
+ console.log(chalk9.yellow("\u26A0\uFE0F tmux is not installed.\n"));
3467
+ const { installTmuxNow } = await inquirer2.prompt([
3468
+ {
3469
+ type: "confirm",
3470
+ name: "installTmuxNow",
3471
+ message: "Install tmux automatically?",
3472
+ default: true
3473
+ }
3474
+ ]);
3475
+ if (installTmuxNow) {
3476
+ const installed = await installTmux();
3477
+ if (!installed) {
3478
+ console.log(chalk9.red("\n\u274C tmux installation failed or was skipped."));
3479
+ console.log(chalk9.dim("Please install tmux manually and run branchnexus init again.\n"));
3480
+ process.exit(1);
3481
+ }
3482
+ } else {
3483
+ console.log(chalk9.dim("\nPlease install tmux manually:"));
3484
+ if (platform2 === "macos" /* MACOS */) {
3485
+ console.log(chalk9.cyan(" brew install tmux"));
3486
+ } else {
3487
+ console.log(chalk9.cyan(" sudo apt-get install tmux"));
3488
+ }
3489
+ console.log();
3490
+ process.exit(1);
3491
+ }
3492
+ } else {
3493
+ console.log(chalk9.green("\u2705 tmux is installed\n"));
3494
+ }
3495
+ const answers = await inquirer2.prompt([
3496
+ {
3497
+ type: "input",
3498
+ name: "defaultRoot",
3499
+ message: "Default working directory:",
3500
+ default: currentConfig.defaultRoot || "~/workspace"
3501
+ },
3502
+ {
3503
+ type: "list",
3504
+ name: "layout",
3505
+ message: "Default layout:",
3506
+ choices: [
3507
+ { name: "grid (2x2)", value: "grid" },
3508
+ { name: "horizontal (side by side)", value: "horizontal" },
3509
+ { name: "vertical (stacked)", value: "vertical" }
3510
+ ],
3511
+ default: currentConfig.defaultLayout
3512
+ },
3513
+ {
3514
+ type: "number",
3515
+ name: "panes",
3516
+ message: "Default number of panes (2-6):",
3517
+ default: currentConfig.defaultPanes,
3518
+ validate: (value) => {
3519
+ if (value < 2 || value > 6) {
3520
+ return "Panes must be between 2 and 6";
3521
+ }
3522
+ return true;
3523
+ }
3524
+ },
3525
+ {
3526
+ type: "list",
3527
+ name: "cleanup",
3528
+ message: "Cleanup policy:",
3529
+ choices: [
3530
+ { name: "session (delete worktrees on exit)", value: "session" },
3531
+ { name: "persistent (keep worktrees)", value: "persistent" }
3532
+ ],
3533
+ default: currentConfig.cleanupPolicy
3534
+ },
3535
+ ...platform2 === "windows" /* WINDOWS */ ? [
3536
+ {
3537
+ type: "list",
3538
+ name: "wslDistribution",
3539
+ message: "WSL distribution:",
3540
+ choices: async () => {
3541
+ try {
3542
+ const distros = await listDistributions();
3543
+ return distros.map((d) => ({ name: d, value: d }));
3544
+ } catch {
3545
+ return [{ name: "No WSL distributions found", value: "" }];
3546
+ }
3547
+ },
3548
+ default: currentConfig.wslDistribution
3549
+ }
3550
+ ] : [],
3551
+ {
3552
+ type: "confirm",
3553
+ name: "githubTokenPrompt",
3554
+ message: "Configure GitHub token for private repos?",
3555
+ default: false
3556
+ },
3557
+ {
3558
+ type: "password",
3559
+ name: "githubToken",
3560
+ message: "GitHub token (optional):",
3561
+ mask: "*",
3562
+ when: (answers2) => answers2.githubTokenPrompt
3563
+ },
3564
+ {
3565
+ type: "confirm",
3566
+ name: "saveConfig",
3567
+ message: "Save configuration?",
3568
+ default: true
3569
+ }
3570
+ ]);
3571
+ return {
3572
+ defaultRoot: answers.defaultRoot,
3573
+ defaultLayout: answers.layout,
3574
+ defaultPanes: answers.panes,
3575
+ cleanupPolicy: answers.cleanup,
3576
+ wslDistribution: answers.wslDistribution,
3577
+ githubToken: answers.githubToken
3578
+ };
3579
+ }
3580
+ async function initCommand() {
3581
+ logger.info("Starting init wizard");
3582
+ console.log(chalk9.bold("\n\u{1F4CB} BranchNexus Setup\n"));
3583
+ console.log("This wizard will help you configure BranchNexus for your workflow.\n");
3584
+ const partialConfig = await promptSetup();
3585
+ const config = loadConfig();
3586
+ const merged = {
3587
+ ...DEFAULT_CONFIG,
3588
+ ...config,
3589
+ ...partialConfig
3590
+ };
3591
+ if (partialConfig.wslDistribution !== void 0 && partialConfig.wslDistribution !== "") {
3592
+ setWslDistribution(partialConfig.wslDistribution);
3593
+ }
3594
+ if (partialConfig.githubToken !== void 0 && partialConfig.githubToken !== "") {
3595
+ setGithubToken(partialConfig.githubToken);
3596
+ }
3597
+ saveConfig(merged);
3598
+ console.log(chalk9.green("\n\u2705 Configuration saved!\n"));
3599
+ console.log(chalk9.dim(`Config location: ${process.env.HOME}/.config/branchnexus/config.json`));
3600
+ console.log(chalk9.dim("\nRun `branchnexus` to start a session.\n"));
3601
+ }
3602
+
3603
+ // ts-src/cli.ts
3604
+ init_errors();
3605
+ init_logger();
3606
+ var program = new Command();
3607
+ program.name("branchnexus").description("Multi-branch workspace orchestrator for tmux").version("1.0.0");
3608
+ program.command("init").description("First-time setup wizard").action(async () => {
3609
+ await initCommand();
3610
+ });
3611
+ program.command("config").description("Manage configuration (show | set | reset | export | import)").argument("[action]", "show | set | reset | export | import").argument("[key]", "Config key or file path").argument("[value]", "Config value").action((action, key, value) => {
3612
+ configCommand(action, key, value);
3613
+ });
3614
+ program.command("kill").description("Kill an active BranchNexus tmux session").argument("[session]", "Session name to kill").action(async (session) => {
3615
+ await killCommand(session);
3616
+ });
3617
+ program.command("preset").description("Manage presets (list | save | load | delete | rename)").argument("[action]", "list | save | load | delete | rename").argument("[name]", "Preset name").argument("[extra]", "JSON data or new name for rename").action((action, name, extra) => {
3618
+ presetCommand(action, name, extra);
3619
+ });
3620
+ program.command("status").description("Show BranchNexus status overview").action(async () => {
3621
+ await statusCommand();
3622
+ });
3623
+ program.command("attach").description("Attach to a detached BranchNexus tmux session").argument("[session]", "Session name to attach").action(async (session) => {
3624
+ await attachCommand(session);
3625
+ });
3626
+ program.option("--root <path>", "Working directory").option("--layout <layout>", "horizontal | vertical | grid").option("--panes <number>", "Number of panes (2-6)", parseInt).option("--cleanup <policy>", "session | persistent").option("--fresh", "Reset workspace and start fresh").option("--terminal-template <template>", "2 | 4 | 6 | 8 | 12 | 16 | custom").option("--max-terminals <count>", "Maximum terminals", parseInt).option("--log-level <level>", "DEBUG | INFO | WARN | ERROR").option("--log-file <path>", "Log file path").option("--session <name>", "Custom session name").option("--no-hooks", "Skip command hooks");
3627
+ program.action(async (options) => {
3628
+ await runCommand2(options);
3629
+ });
3630
+ async function main() {
3631
+ const logPath = defaultLogPath();
3632
+ configureLogging({ logFile: logPath });
3633
+ try {
3634
+ await program.parseAsync(process.argv);
3635
+ } catch (error) {
3636
+ if (isBranchNexusError(error)) {
3637
+ logger.error(`BranchNexusError: ${error.message}`);
3638
+ console.error(chalk9.red(userFacingError(error.message, error.hint)));
3639
+ process.exit(error.code);
3640
+ }
3641
+ const message = error instanceof Error ? error.message : String(error);
3642
+ logger.error(`Unexpected error: ${message}`);
3643
+ console.error(
3644
+ chalk9.red(userFacingError("Unexpected runtime failure", `Check logs: ${logPath}`))
3645
+ );
3646
+ process.exit(4 /* RUNTIME_ERROR */);
3647
+ }
3648
+ }
3649
+ void main();
3650
+ //# sourceMappingURL=cli.js.map
3651
+ //# sourceMappingURL=cli.js.map