@ondrej-svec/hog 1.22.0 → 1.23.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 CHANGED
@@ -2942,12 +2942,41 @@ function useKeyboard({
2942
2942
  nav.moveUp();
2943
2943
  return;
2944
2944
  }
2945
+ if (input2 === "q") {
2946
+ exit();
2947
+ return;
2948
+ }
2949
+ if (input2 === "/") {
2950
+ multiSelect.clear();
2951
+ ui.enterSearch();
2952
+ return;
2953
+ }
2954
+ if (input2 === "t") {
2955
+ handleToggleMine();
2956
+ return;
2957
+ }
2958
+ if (input2 === "F") {
2959
+ handleEnterFuzzyPicker();
2960
+ return;
2961
+ }
2945
2962
  if (input2 === "C") {
2946
2963
  handleLaunchClaude();
2947
2964
  return;
2948
2965
  }
2949
- if (input2 === "q") {
2950
- exit();
2966
+ if (input2 === "y") {
2967
+ handleCopyLink();
2968
+ return;
2969
+ }
2970
+ if (input2 === "o") {
2971
+ handleSlack();
2972
+ return;
2973
+ }
2974
+ if (input2 === "g") {
2975
+ handleOpen();
2976
+ return;
2977
+ }
2978
+ if (input2 === "r" || input2 === "R") {
2979
+ refresh();
2951
2980
  return;
2952
2981
  }
2953
2982
  return;
@@ -4081,42 +4110,6 @@ function isPaneAlive(paneId) {
4081
4110
  return false;
4082
4111
  }
4083
4112
  }
4084
- function splitWithInfo(info, widthPercent) {
4085
- try {
4086
- const paneId = execFileSync3(
4087
- "tmux",
4088
- [
4089
- "split-window",
4090
- "-h",
4091
- "-l",
4092
- `${widthPercent}%`,
4093
- "-d",
4094
- "-P",
4095
- "-F",
4096
- "#{pane_id}",
4097
- "printf",
4098
- "Issue: %s\\n\\nURL: %s\\n\\nNo Claude Code session active.\\nPress C to launch one.\\n",
4099
- info.title,
4100
- info.url
4101
- ],
4102
- {
4103
- encoding: "utf-8",
4104
- stdio: ["ignore", "pipe", "ignore"]
4105
- }
4106
- );
4107
- return paneId.trim() || null;
4108
- } catch {
4109
- return null;
4110
- }
4111
- }
4112
- function killPane(paneId) {
4113
- try {
4114
- execFileSync3("tmux", ["kill-pane", "-t", paneId], {
4115
- stdio: "ignore"
4116
- });
4117
- } catch {
4118
- }
4119
- }
4120
4113
  var init_tmux_pane = __esm({
4121
4114
  "src/board/tmux-pane.ts"() {
4122
4115
  "use strict";
@@ -4125,32 +4118,16 @@ var init_tmux_pane = __esm({
4125
4118
 
4126
4119
  // src/board/hooks/use-zen-mode.ts
4127
4120
  import { useCallback as useCallback13, useEffect as useEffect5, useRef as useRef11, useState as useState7 } from "react";
4128
- function findIssue(repos, selectedId) {
4121
+ function issueNumberFromId(selectedId) {
4129
4122
  if (!selectedId?.startsWith("gh:")) return null;
4130
- for (const rd of repos) {
4131
- for (const issue of rd.issues) {
4132
- if (`gh:${rd.repo.name}:${issue.number}` === selectedId)
4133
- return { issue, repoName: rd.repo.name };
4134
- }
4135
- }
4136
- return null;
4137
- }
4138
- function cleanupPane(paneId, isAgent) {
4139
- if (isAgent) {
4140
- breakPane(paneId);
4141
- } else {
4142
- killPane(paneId);
4143
- }
4123
+ const parts = selectedId.split(":");
4124
+ const num = Number(parts[2]);
4125
+ return Number.isNaN(num) ? null : num;
4144
4126
  }
4145
- function openOrSplitPane(issue) {
4146
- const winName = agentWindowName(issue.number);
4147
- const hasAgent = windowExists(winName);
4148
- if (hasAgent) {
4149
- const paneId2 = joinAgentPane(winName, ZEN_PANE_WIDTH_PERCENT);
4150
- return paneId2 ? { paneId: paneId2, isAgent: true } : null;
4151
- }
4152
- const paneId = splitWithInfo({ title: issue.title, url: issue.url }, ZEN_PANE_WIDTH_PERCENT);
4153
- return paneId ? { paneId, isAgent: false } : null;
4127
+ function tryJoinAgent(issueNumber) {
4128
+ const winName = agentWindowName(issueNumber);
4129
+ if (!windowExists(winName)) return null;
4130
+ return joinAgentPane(winName, ZEN_PANE_WIDTH_PERCENT);
4154
4131
  }
4155
4132
  function useZenMode({
4156
4133
  ui,
@@ -4160,21 +4137,19 @@ function useZenMode({
4160
4137
  selectedId
4161
4138
  }) {
4162
4139
  const [zenPaneId, setZenPaneId] = useState7(null);
4163
- const [zenIsAgentPane, setZenIsAgentPane] = useState7(false);
4164
- const paneRef = useRef11({
4165
- id: null,
4166
- isAgent: false
4167
- });
4168
- paneRef.current = { id: zenPaneId, isAgent: zenIsAgentPane };
4169
- const exitZen = useCallback13(() => {
4170
- const { id, isAgent } = paneRef.current;
4140
+ const paneRef = useRef11(null);
4141
+ paneRef.current = zenPaneId;
4142
+ const cleanupCurrentPane = useCallback13(() => {
4143
+ const id = paneRef.current;
4171
4144
  if (id) {
4172
- cleanupPane(id, isAgent);
4145
+ breakPane(id);
4173
4146
  setZenPaneId(null);
4174
- setZenIsAgentPane(false);
4175
4147
  }
4148
+ }, []);
4149
+ const exitZen = useCallback13(() => {
4150
+ cleanupCurrentPane();
4176
4151
  ui.exitZen();
4177
- }, [ui]);
4152
+ }, [ui, cleanupCurrentPane]);
4178
4153
  const handleToggleZen = useCallback13(() => {
4179
4154
  if (ui.state.mode === "zen") {
4180
4155
  exitZen();
@@ -4188,70 +4163,54 @@ function useZenMode({
4188
4163
  toast.error("Terminal too narrow for Zen mode");
4189
4164
  return;
4190
4165
  }
4191
- const found = findIssue(repos, selectedId);
4192
- const result = found ? openOrSplitPane(found.issue) : null;
4193
- if (!result) {
4194
- toast.error("Failed to create tmux pane");
4195
- return;
4166
+ const issueNum = issueNumberFromId(selectedId);
4167
+ if (issueNum) {
4168
+ const paneId = tryJoinAgent(issueNum);
4169
+ if (paneId) setZenPaneId(paneId);
4196
4170
  }
4197
- setZenPaneId(result.paneId);
4198
- setZenIsAgentPane(result.isAgent);
4199
4171
  ui.enterZen();
4200
- }, [ui, toast, termCols, exitZen, repos, selectedId]);
4172
+ }, [ui, toast, termCols, exitZen, selectedId]);
4201
4173
  const swapToAgent = useCallback13(
4202
4174
  (issueNumber) => {
4203
- const { id, isAgent } = paneRef.current;
4204
- if (ui.state.mode !== "zen" || !id) return;
4205
- cleanupPane(id, isAgent);
4175
+ if (ui.state.mode !== "zen") return;
4176
+ cleanupCurrentPane();
4206
4177
  setTimeout(() => {
4207
- const winName = agentWindowName(issueNumber);
4208
- if (windowExists(winName)) {
4209
- const newPaneId = joinAgentPane(winName, ZEN_PANE_WIDTH_PERCENT);
4210
- setZenPaneId(newPaneId);
4211
- setZenIsAgentPane(true);
4212
- }
4178
+ const paneId = tryJoinAgent(issueNumber);
4179
+ if (paneId) setZenPaneId(paneId);
4213
4180
  }, 500);
4214
4181
  },
4215
- [ui.state.mode]
4182
+ [ui.state.mode, cleanupCurrentPane]
4216
4183
  );
4217
4184
  const prevSelectedRef = useRef11(null);
4218
4185
  useEffect5(() => {
4219
- if (ui.state.mode !== "zen" || !zenPaneId) {
4186
+ if (ui.state.mode !== "zen") {
4220
4187
  prevSelectedRef.current = selectedId;
4221
4188
  return;
4222
4189
  }
4223
4190
  if (selectedId === prevSelectedRef.current) return;
4224
4191
  prevSelectedRef.current = selectedId;
4225
4192
  const timer = setTimeout(() => {
4226
- const { id, isAgent } = paneRef.current;
4227
- if (!id) return;
4228
- const found = findIssue(repos, selectedId);
4229
- if (!found) return;
4230
- cleanupPane(id, isAgent);
4231
- const result = openOrSplitPane(found.issue);
4232
- if (!result) {
4233
- setZenPaneId(null);
4234
- setZenIsAgentPane(false);
4235
- ui.exitZen();
4236
- toast.error("Zen pane lost \u2014 exiting zen mode");
4237
- return;
4193
+ const currentId = paneRef.current;
4194
+ if (currentId) {
4195
+ breakPane(currentId);
4238
4196
  }
4239
- setZenPaneId(result.paneId);
4240
- setZenIsAgentPane(result.isAgent);
4197
+ const issueNum = issueNumberFromId(selectedId);
4198
+ const newPaneId = issueNum ? tryJoinAgent(issueNum) : null;
4199
+ setZenPaneId(newPaneId);
4241
4200
  }, CURSOR_FOLLOW_DEBOUNCE_MS);
4242
4201
  return () => clearTimeout(timer);
4243
- }, [ui.state.mode, zenPaneId, selectedId, repos, ui, toast]);
4202
+ }, [ui.state.mode, selectedId, repos]);
4244
4203
  useEffect5(() => {
4245
4204
  if (ui.state.mode !== "zen" || !zenPaneId) return;
4246
4205
  const interval = setInterval(() => {
4247
4206
  if (!isPaneAlive(zenPaneId)) {
4248
- exitZen();
4249
- toast.info("Zen pane closed");
4207
+ setZenPaneId(null);
4208
+ toast.info("Agent pane closed");
4250
4209
  }
4251
4210
  }, DEAD_PANE_CHECK_MS);
4252
4211
  return () => clearInterval(interval);
4253
- }, [ui.state.mode, zenPaneId, exitZen, toast]);
4254
- return { zenPaneId, zenIsAgentPane, handleToggleZen, swapToAgent };
4212
+ }, [ui.state.mode, zenPaneId, toast]);
4213
+ return { zenPaneId, handleToggleZen, swapToAgent };
4255
4214
  }
4256
4215
  var ZEN_PANE_WIDTH_PERCENT, ZEN_MIN_COLS, CURSOR_FOLLOW_DEBOUNCE_MS, DEAD_PANE_CHECK_MS;
4257
4216
  var init_use_zen_mode = __esm({
@@ -4260,7 +4219,7 @@ var init_use_zen_mode = __esm({
4260
4219
  init_tmux_pane();
4261
4220
  ZEN_PANE_WIDTH_PERCENT = 65;
4262
4221
  ZEN_MIN_COLS = 100;
4263
- CURSOR_FOLLOW_DEBOUNCE_MS = 150;
4222
+ CURSOR_FOLLOW_DEBOUNCE_MS = 200;
4264
4223
  DEAD_PANE_CHECK_MS = 2e3;
4265
4224
  }
4266
4225
  });
@@ -4607,7 +4566,10 @@ function HintBar({
4607
4566
  if (uiMode === "zen") {
4608
4567
  return /* @__PURE__ */ jsxs6(Box6, { children: [
4609
4568
  /* @__PURE__ */ jsx6(Text6, { color: "green", bold: true, children: "[ZEN]" }),
4610
- /* @__PURE__ */ jsx6(Text6, { color: "gray", children: " j/k:nav C:claude Z/Esc:exit q:quit" })
4569
+ /* @__PURE__ */ jsxs6(Text6, { color: "gray", children: [
4570
+ " ",
4571
+ "j/k:nav /:search t:mine F:find C:claude y:copy g:open r:refresh Z/Esc:exit q:quit"
4572
+ ] })
4611
4573
  ] });
4612
4574
  }
4613
4575
  if (uiMode === "focus") {
@@ -8313,17 +8275,46 @@ function Dashboard({ config: config2, options, activeProfile }) {
8313
8275
  commentsState: currentCommentsState
8314
8276
  }
8315
8277
  ) : null,
8316
- ui.state.mode === "zen" ? /* @__PURE__ */ jsx29(Box28, { flexDirection: "column", height: issuesPanelHeight + ACTIVITY_HEIGHT, children: /* @__PURE__ */ jsx29(Panel, { title: "Issues (Zen)", isActive: true, width: usableWidth, children: visibleRows.map((row) => /* @__PURE__ */ jsx29(
8317
- RowRenderer,
8318
- {
8319
- row,
8320
- selectedId: nav.selectedId,
8321
- selfLogin: config2.board.assignee,
8322
- panelWidth: usableWidth,
8323
- stalenessConfig: config2.board.workflow?.staleness
8324
- },
8325
- row.key
8326
- )) }) }) : null,
8278
+ ui.state.mode === "zen" ? zen.zenPaneId ? (
8279
+ // Agent pane is joined — show narrow issue list (agent fills the rest via tmux)
8280
+ /* @__PURE__ */ jsx29(Box28, { flexDirection: "column", height: issuesPanelHeight + ACTIVITY_HEIGHT, children: /* @__PURE__ */ jsx29(Panel, { title: "Issues (Zen)", isActive: true, width: usableWidth, children: visibleRows.map((row) => /* @__PURE__ */ jsx29(
8281
+ RowRenderer,
8282
+ {
8283
+ row,
8284
+ selectedId: nav.selectedId,
8285
+ selfLogin: config2.board.assignee,
8286
+ panelWidth: usableWidth,
8287
+ stalenessConfig: config2.board.workflow?.staleness
8288
+ },
8289
+ row.key
8290
+ )) }) })
8291
+ ) : (
8292
+ // No agent — show issue list + detail panel side by side
8293
+ /* @__PURE__ */ jsxs29(Box28, { flexDirection: "row", height: issuesPanelHeight + ACTIVITY_HEIGHT, children: [
8294
+ /* @__PURE__ */ jsx29(Box28, { flexDirection: "column", width: issuesPanelWidth, children: /* @__PURE__ */ jsx29(Panel, { title: "Issues (Zen)", isActive: true, width: issuesPanelWidth, children: visibleRows.map((row) => /* @__PURE__ */ jsx29(
8295
+ RowRenderer,
8296
+ {
8297
+ row,
8298
+ selectedId: nav.selectedId,
8299
+ selfLogin: config2.board.assignee,
8300
+ panelWidth: issuesPanelWidth,
8301
+ stalenessConfig: config2.board.workflow?.staleness
8302
+ },
8303
+ row.key
8304
+ )) }) }),
8305
+ /* @__PURE__ */ jsx29(
8306
+ DetailPanel,
8307
+ {
8308
+ issue: selectedItem.issue,
8309
+ width: detailPanelWidth,
8310
+ isActive: false,
8311
+ issueRepo: selectedItem.repoName,
8312
+ fetchComments: handleFetchComments,
8313
+ commentsState: currentCommentsState
8314
+ }
8315
+ )
8316
+ ] })
8317
+ ) : null,
8327
8318
  !ui.state.helpVisible && ui.state.mode !== "overlay:status" && ui.state.mode !== "overlay:create" && ui.state.mode !== "overlay:createNl" && ui.state.mode !== "overlay:bulkAction" && ui.state.mode !== "overlay:confirmPick" && ui.state.mode !== "overlay:detail" && ui.state.mode !== "overlay:nudge" && ui.state.mode !== "overlay:triage" && ui.state.mode !== "focus" && ui.state.mode !== "zen" ? /* @__PURE__ */ jsx29(
8328
8319
  PanelLayout,
8329
8320
  {
@@ -9416,7 +9407,7 @@ async function resolveRef(ref, config2) {
9416
9407
  }
9417
9408
  }
9418
9409
  var program = new Command();
9419
- program.name("hog").description("Personal command deck \u2014 GitHub Projects dashboard with workflow orchestration").version("1.22.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
9410
+ program.name("hog").description("Personal command deck \u2014 GitHub Projects dashboard with workflow orchestration").version("1.23.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
9420
9411
  const opts = thisCommand.opts();
9421
9412
  if (opts.json) setFormat("json");
9422
9413
  if (opts.human) setFormat("human");