@ondrej-svec/hog 1.22.1 → 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,44 +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
- "sh",
4098
- "-c",
4099
- 'printf "Issue: %s\\n\\nURL: %s\\n\\nNo Claude Code session active.\\nPress C to launch one.\\n" "$1" "$2"; read _',
4100
- "--",
4101
- info.title,
4102
- info.url
4103
- ],
4104
- {
4105
- encoding: "utf-8",
4106
- stdio: ["ignore", "pipe", "ignore"]
4107
- }
4108
- );
4109
- return paneId.trim() || null;
4110
- } catch {
4111
- return null;
4112
- }
4113
- }
4114
- function killPane(paneId) {
4115
- try {
4116
- execFileSync3("tmux", ["kill-pane", "-t", paneId], {
4117
- stdio: "ignore"
4118
- });
4119
- } catch {
4120
- }
4121
- }
4122
4113
  var init_tmux_pane = __esm({
4123
4114
  "src/board/tmux-pane.ts"() {
4124
4115
  "use strict";
@@ -4127,32 +4118,16 @@ var init_tmux_pane = __esm({
4127
4118
 
4128
4119
  // src/board/hooks/use-zen-mode.ts
4129
4120
  import { useCallback as useCallback13, useEffect as useEffect5, useRef as useRef11, useState as useState7 } from "react";
4130
- function findIssue(repos, selectedId) {
4121
+ function issueNumberFromId(selectedId) {
4131
4122
  if (!selectedId?.startsWith("gh:")) return null;
4132
- for (const rd of repos) {
4133
- for (const issue of rd.issues) {
4134
- if (`gh:${rd.repo.name}:${issue.number}` === selectedId)
4135
- return { issue, repoName: rd.repo.name };
4136
- }
4137
- }
4138
- return null;
4139
- }
4140
- function cleanupPane(paneId, isAgent) {
4141
- if (isAgent) {
4142
- breakPane(paneId);
4143
- } else {
4144
- killPane(paneId);
4145
- }
4123
+ const parts = selectedId.split(":");
4124
+ const num = Number(parts[2]);
4125
+ return Number.isNaN(num) ? null : num;
4146
4126
  }
4147
- function openOrSplitPane(issue) {
4148
- const winName = agentWindowName(issue.number);
4149
- const hasAgent = windowExists(winName);
4150
- if (hasAgent) {
4151
- const paneId2 = joinAgentPane(winName, ZEN_PANE_WIDTH_PERCENT);
4152
- return paneId2 ? { paneId: paneId2, isAgent: true } : null;
4153
- }
4154
- const paneId = splitWithInfo({ title: issue.title, url: issue.url }, ZEN_PANE_WIDTH_PERCENT);
4155
- 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);
4156
4131
  }
4157
4132
  function useZenMode({
4158
4133
  ui,
@@ -4162,21 +4137,19 @@ function useZenMode({
4162
4137
  selectedId
4163
4138
  }) {
4164
4139
  const [zenPaneId, setZenPaneId] = useState7(null);
4165
- const [zenIsAgentPane, setZenIsAgentPane] = useState7(false);
4166
- const paneRef = useRef11({
4167
- id: null,
4168
- isAgent: false
4169
- });
4170
- paneRef.current = { id: zenPaneId, isAgent: zenIsAgentPane };
4171
- const exitZen = useCallback13(() => {
4172
- const { id, isAgent } = paneRef.current;
4140
+ const paneRef = useRef11(null);
4141
+ paneRef.current = zenPaneId;
4142
+ const cleanupCurrentPane = useCallback13(() => {
4143
+ const id = paneRef.current;
4173
4144
  if (id) {
4174
- cleanupPane(id, isAgent);
4145
+ breakPane(id);
4175
4146
  setZenPaneId(null);
4176
- setZenIsAgentPane(false);
4177
4147
  }
4148
+ }, []);
4149
+ const exitZen = useCallback13(() => {
4150
+ cleanupCurrentPane();
4178
4151
  ui.exitZen();
4179
- }, [ui]);
4152
+ }, [ui, cleanupCurrentPane]);
4180
4153
  const handleToggleZen = useCallback13(() => {
4181
4154
  if (ui.state.mode === "zen") {
4182
4155
  exitZen();
@@ -4190,70 +4163,54 @@ function useZenMode({
4190
4163
  toast.error("Terminal too narrow for Zen mode");
4191
4164
  return;
4192
4165
  }
4193
- const found = findIssue(repos, selectedId);
4194
- const result = found ? openOrSplitPane(found.issue) : null;
4195
- if (!result) {
4196
- toast.error("Failed to create tmux pane");
4197
- return;
4166
+ const issueNum = issueNumberFromId(selectedId);
4167
+ if (issueNum) {
4168
+ const paneId = tryJoinAgent(issueNum);
4169
+ if (paneId) setZenPaneId(paneId);
4198
4170
  }
4199
- setZenPaneId(result.paneId);
4200
- setZenIsAgentPane(result.isAgent);
4201
4171
  ui.enterZen();
4202
- }, [ui, toast, termCols, exitZen, repos, selectedId]);
4172
+ }, [ui, toast, termCols, exitZen, selectedId]);
4203
4173
  const swapToAgent = useCallback13(
4204
4174
  (issueNumber) => {
4205
- const { id, isAgent } = paneRef.current;
4206
- if (ui.state.mode !== "zen" || !id) return;
4207
- cleanupPane(id, isAgent);
4175
+ if (ui.state.mode !== "zen") return;
4176
+ cleanupCurrentPane();
4208
4177
  setTimeout(() => {
4209
- const winName = agentWindowName(issueNumber);
4210
- if (windowExists(winName)) {
4211
- const newPaneId = joinAgentPane(winName, ZEN_PANE_WIDTH_PERCENT);
4212
- setZenPaneId(newPaneId);
4213
- setZenIsAgentPane(true);
4214
- }
4178
+ const paneId = tryJoinAgent(issueNumber);
4179
+ if (paneId) setZenPaneId(paneId);
4215
4180
  }, 500);
4216
4181
  },
4217
- [ui.state.mode]
4182
+ [ui.state.mode, cleanupCurrentPane]
4218
4183
  );
4219
4184
  const prevSelectedRef = useRef11(null);
4220
4185
  useEffect5(() => {
4221
- if (ui.state.mode !== "zen" || !zenPaneId) {
4186
+ if (ui.state.mode !== "zen") {
4222
4187
  prevSelectedRef.current = selectedId;
4223
4188
  return;
4224
4189
  }
4225
4190
  if (selectedId === prevSelectedRef.current) return;
4226
4191
  prevSelectedRef.current = selectedId;
4227
4192
  const timer = setTimeout(() => {
4228
- const { id, isAgent } = paneRef.current;
4229
- if (!id) return;
4230
- const found = findIssue(repos, selectedId);
4231
- if (!found) return;
4232
- cleanupPane(id, isAgent);
4233
- const result = openOrSplitPane(found.issue);
4234
- if (!result) {
4235
- setZenPaneId(null);
4236
- setZenIsAgentPane(false);
4237
- ui.exitZen();
4238
- toast.error("Zen pane lost \u2014 exiting zen mode");
4239
- return;
4193
+ const currentId = paneRef.current;
4194
+ if (currentId) {
4195
+ breakPane(currentId);
4240
4196
  }
4241
- setZenPaneId(result.paneId);
4242
- setZenIsAgentPane(result.isAgent);
4197
+ const issueNum = issueNumberFromId(selectedId);
4198
+ const newPaneId = issueNum ? tryJoinAgent(issueNum) : null;
4199
+ setZenPaneId(newPaneId);
4243
4200
  }, CURSOR_FOLLOW_DEBOUNCE_MS);
4244
4201
  return () => clearTimeout(timer);
4245
- }, [ui.state.mode, zenPaneId, selectedId, repos, ui, toast]);
4202
+ }, [ui.state.mode, selectedId, repos]);
4246
4203
  useEffect5(() => {
4247
4204
  if (ui.state.mode !== "zen" || !zenPaneId) return;
4248
4205
  const interval = setInterval(() => {
4249
4206
  if (!isPaneAlive(zenPaneId)) {
4250
- exitZen();
4251
- toast.info("Zen pane closed");
4207
+ setZenPaneId(null);
4208
+ toast.info("Agent pane closed");
4252
4209
  }
4253
4210
  }, DEAD_PANE_CHECK_MS);
4254
4211
  return () => clearInterval(interval);
4255
- }, [ui.state.mode, zenPaneId, exitZen, toast]);
4256
- return { zenPaneId, zenIsAgentPane, handleToggleZen, swapToAgent };
4212
+ }, [ui.state.mode, zenPaneId, toast]);
4213
+ return { zenPaneId, handleToggleZen, swapToAgent };
4257
4214
  }
4258
4215
  var ZEN_PANE_WIDTH_PERCENT, ZEN_MIN_COLS, CURSOR_FOLLOW_DEBOUNCE_MS, DEAD_PANE_CHECK_MS;
4259
4216
  var init_use_zen_mode = __esm({
@@ -4262,7 +4219,7 @@ var init_use_zen_mode = __esm({
4262
4219
  init_tmux_pane();
4263
4220
  ZEN_PANE_WIDTH_PERCENT = 65;
4264
4221
  ZEN_MIN_COLS = 100;
4265
- CURSOR_FOLLOW_DEBOUNCE_MS = 150;
4222
+ CURSOR_FOLLOW_DEBOUNCE_MS = 200;
4266
4223
  DEAD_PANE_CHECK_MS = 2e3;
4267
4224
  }
4268
4225
  });
@@ -4609,7 +4566,10 @@ function HintBar({
4609
4566
  if (uiMode === "zen") {
4610
4567
  return /* @__PURE__ */ jsxs6(Box6, { children: [
4611
4568
  /* @__PURE__ */ jsx6(Text6, { color: "green", bold: true, children: "[ZEN]" }),
4612
- /* @__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
+ ] })
4613
4573
  ] });
4614
4574
  }
4615
4575
  if (uiMode === "focus") {
@@ -8315,17 +8275,46 @@ function Dashboard({ config: config2, options, activeProfile }) {
8315
8275
  commentsState: currentCommentsState
8316
8276
  }
8317
8277
  ) : null,
8318
- 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(
8319
- RowRenderer,
8320
- {
8321
- row,
8322
- selectedId: nav.selectedId,
8323
- selfLogin: config2.board.assignee,
8324
- panelWidth: usableWidth,
8325
- stalenessConfig: config2.board.workflow?.staleness
8326
- },
8327
- row.key
8328
- )) }) }) : 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,
8329
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(
8330
8319
  PanelLayout,
8331
8320
  {
@@ -9418,7 +9407,7 @@ async function resolveRef(ref, config2) {
9418
9407
  }
9419
9408
  }
9420
9409
  var program = new Command();
9421
- program.name("hog").description("Personal command deck \u2014 GitHub Projects dashboard with workflow orchestration").version("1.22.1").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) => {
9422
9411
  const opts = thisCommand.opts();
9423
9412
  if (opts.json) setFormat("json");
9424
9413
  if (opts.human) setFormat("human");