@ondrej-svec/hog 1.15.0 → 1.16.1

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
@@ -2149,6 +2149,29 @@ function useKeyboard({
2149
2149
  if (handleErrorAction("dismiss")) return;
2150
2150
  }
2151
2151
  if (input2 === "r" && handleErrorAction("retry")) return;
2152
+ if (ui.canAct || ui.state.mode === "overlay:detail") {
2153
+ if (input2 === "y") {
2154
+ handleCopyLink();
2155
+ return;
2156
+ }
2157
+ if (input2 === "o") {
2158
+ handleSlack();
2159
+ return;
2160
+ }
2161
+ if (input2 === "c") {
2162
+ if (selectedIssue) {
2163
+ multiSelect.clear();
2164
+ ui.enterComment();
2165
+ }
2166
+ return;
2167
+ }
2168
+ if (input2 === "e") {
2169
+ if (selectedIssue) {
2170
+ handleEnterEditIssue();
2171
+ }
2172
+ return;
2173
+ }
2174
+ }
2152
2175
  if (ui.canAct) {
2153
2176
  const digit = parseInt(input2, 10);
2154
2177
  if (!Number.isNaN(digit) && digit >= 0 && digit <= 4) {
@@ -2173,14 +2196,6 @@ function useKeyboard({
2173
2196
  refresh();
2174
2197
  return;
2175
2198
  }
2176
- if (input2 === "o") {
2177
- handleSlack();
2178
- return;
2179
- }
2180
- if (input2 === "y") {
2181
- handleCopyLink();
2182
- return;
2183
- }
2184
2199
  if (input2 === "p") {
2185
2200
  handlePick();
2186
2201
  return;
@@ -2197,13 +2212,6 @@ function useKeyboard({
2197
2212
  handleToggleLog();
2198
2213
  return;
2199
2214
  }
2200
- if (input2 === "c") {
2201
- if (selectedIssue) {
2202
- multiSelect.clear();
2203
- ui.enterComment();
2204
- }
2205
- return;
2206
- }
2207
2215
  if (input2 === "m") {
2208
2216
  if (selectedIssue && selectedRepoStatusOptionsLength > 0) {
2209
2217
  multiSelect.clear();
@@ -2241,12 +2249,6 @@ function useKeyboard({
2241
2249
  handleEnterFuzzyPicker();
2242
2250
  return;
2243
2251
  }
2244
- if (input2 === "e") {
2245
- if (selectedIssue) {
2246
- handleEnterEditIssue();
2247
- }
2248
- return;
2249
- }
2250
2252
  if (input2 === " ") {
2251
2253
  const id = nav.selectedId;
2252
2254
  if (id) {
@@ -2306,7 +2308,8 @@ function useKeyboard({
2306
2308
  handleEnterFuzzyPicker,
2307
2309
  handleEnterEditIssue,
2308
2310
  handleUndo,
2309
- handleToggleLog
2311
+ handleToggleLog,
2312
+ showDetailPanel
2310
2313
  ]
2311
2314
  );
2312
2315
  const inputActive = ui.state.mode === "normal" || ui.state.mode === "multiSelect" || ui.state.mode === "focus" || ui.state.mode === "overlay:detail";
@@ -6850,6 +6853,145 @@ Config written to ${CONFIG_DIR}/config.json`);
6850
6853
  console.log(" hog task list # List TickTick tasks");
6851
6854
  console.log(" hog config show # View configuration\n");
6852
6855
  }
6856
+ async function runReposAdd(initialRepoName) {
6857
+ try {
6858
+ await runReposAddWizard(initialRepoName);
6859
+ } catch (error) {
6860
+ if (error instanceof Error && error.message.includes("User force closed")) {
6861
+ console.log("\nCancelled. No changes were made.");
6862
+ return;
6863
+ }
6864
+ throw error;
6865
+ }
6866
+ }
6867
+ async function runReposAddWizard(initialRepoName) {
6868
+ console.log("\n\u{1F417} hog config repos:add\n");
6869
+ const cfg = loadFullConfig();
6870
+ let repoName = initialRepoName;
6871
+ if (!repoName) {
6872
+ console.log("Fetching repositories...");
6873
+ const allRepos = listAllRepos();
6874
+ const configuredNames = new Set(cfg.repos.map((r) => r.name));
6875
+ const available = allRepos.filter((r) => !configuredNames.has(r.nameWithOwner));
6876
+ if (available.length === 0) {
6877
+ console.log(
6878
+ "No more repositories available to add. All accessible repos are already tracked."
6879
+ );
6880
+ return;
6881
+ }
6882
+ repoName = await select({
6883
+ message: "Select repository to add:",
6884
+ choices: available.map((r) => ({ name: r.nameWithOwner, value: r.nameWithOwner }))
6885
+ });
6886
+ }
6887
+ if (!validateRepoName(repoName)) {
6888
+ console.error("Invalid repo name. Use owner/repo format (e.g. myorg/myrepo).");
6889
+ process.exit(1);
6890
+ }
6891
+ if (findRepo(cfg, repoName)) {
6892
+ console.error(`Repo "${repoName}" is already configured.`);
6893
+ process.exit(1);
6894
+ }
6895
+ const [owner, name] = repoName.split("/");
6896
+ console.log(`
6897
+ Configuring ${repoName}...`);
6898
+ console.log(" Fetching GitHub Projects...");
6899
+ const projects = listOrgProjects(owner);
6900
+ let projectNumber;
6901
+ if (projects.length === 0) {
6902
+ console.log(" No GitHub Projects found. Enter project number manually.");
6903
+ const num = await input({ message: ` Project number for ${repoName}:` });
6904
+ projectNumber = Number.parseInt(num, 10);
6905
+ } else {
6906
+ projectNumber = await select({
6907
+ message: ` Select project for ${repoName}:`,
6908
+ choices: projects.map((p) => ({ name: `#${p.number} \u2014 ${p.title}`, value: p.number }))
6909
+ });
6910
+ }
6911
+ console.log(" Detecting status field...");
6912
+ const statusInfo = detectStatusField(owner, projectNumber);
6913
+ let statusFieldId;
6914
+ if (statusInfo) {
6915
+ statusFieldId = statusInfo.fieldId;
6916
+ console.log(` Found status field: ${statusFieldId}`);
6917
+ } else {
6918
+ console.log(" Could not auto-detect status field.");
6919
+ statusFieldId = await input({ message: " Enter status field ID manually:" });
6920
+ }
6921
+ console.log(" Detecting due date field...");
6922
+ let dueDateFieldId;
6923
+ const existingDateField = detectDateField(owner, projectNumber);
6924
+ if (existingDateField) {
6925
+ console.log(` Found date field: "${existingDateField.name}" (${existingDateField.id})`);
6926
+ const useDateField = await confirm({
6927
+ message: ` Use "${existingDateField.name}" for due dates?`,
6928
+ default: true
6929
+ });
6930
+ if (useDateField) {
6931
+ dueDateFieldId = existingDateField.id;
6932
+ }
6933
+ } else {
6934
+ console.log(" No due date field found.");
6935
+ const createField = await confirm({
6936
+ message: ' Create a "Due Date" field for this project?',
6937
+ default: false
6938
+ });
6939
+ if (createField) {
6940
+ console.log(' Creating "Due Date" field...');
6941
+ const newFieldId = createDateField(owner, projectNumber, "Due Date");
6942
+ if (newFieldId) {
6943
+ dueDateFieldId = newFieldId;
6944
+ console.log(` Created "Due Date" field (${newFieldId})`);
6945
+ } else {
6946
+ console.log(" Could not create field \u2014 due dates will be stored in issue body.");
6947
+ }
6948
+ }
6949
+ }
6950
+ const completionType = await select({
6951
+ message: " When a task is completed, what should happen on GitHub?",
6952
+ choices: [
6953
+ { name: "Close the issue", value: "closeIssue" },
6954
+ { name: "Add a label (e.g. review:pending)", value: "addLabel" },
6955
+ { name: "Update project status column", value: "updateProjectStatus" }
6956
+ ]
6957
+ });
6958
+ let completionAction;
6959
+ if (completionType === "addLabel") {
6960
+ const label = await input({ message: " Label to add:", default: "review:pending" });
6961
+ completionAction = { type: "addLabel", label };
6962
+ } else if (completionType === "updateProjectStatus") {
6963
+ const statusOptions = statusInfo?.options ?? [];
6964
+ let optionId;
6965
+ if (statusOptions.length > 0) {
6966
+ optionId = await select({
6967
+ message: " Status to set when completed:",
6968
+ choices: statusOptions.map((o) => ({ name: o.name, value: o.id }))
6969
+ });
6970
+ } else {
6971
+ optionId = await input({ message: " Status option ID to set:" });
6972
+ }
6973
+ completionAction = { type: "updateProjectStatus", optionId };
6974
+ } else {
6975
+ completionAction = { type: "closeIssue" };
6976
+ }
6977
+ const shortName2 = await input({
6978
+ message: ` Short name for ${repoName}:`,
6979
+ default: name
6980
+ });
6981
+ const newRepo = {
6982
+ name: repoName,
6983
+ shortName: shortName2,
6984
+ projectNumber,
6985
+ statusFieldId,
6986
+ ...dueDateFieldId ? { dueDateFieldId } : {},
6987
+ completionAction
6988
+ };
6989
+ cfg.repos.push(newRepo);
6990
+ saveFullConfig(cfg);
6991
+ console.log(`
6992
+ Added ${shortName2} \u2192 ${repoName}`);
6993
+ console.log(" Run: hog board --live\n");
6994
+ }
6853
6995
 
6854
6996
  // src/cli.ts
6855
6997
  init_log_persistence();
@@ -7273,7 +7415,7 @@ function resolveProjectId(projectId) {
7273
7415
  process.exit(1);
7274
7416
  }
7275
7417
  var program = new Command();
7276
- program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.15.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
7418
+ program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.16.1").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
7277
7419
  const opts = thisCommand.opts();
7278
7420
  if (opts.json) setFormat("json");
7279
7421
  if (opts.human) setFormat("human");
@@ -7455,10 +7597,18 @@ config.command("repos").description("List configured repositories").action(() =>
7455
7597
  }
7456
7598
  }
7457
7599
  });
7458
- config.command("repos:add <name>").description("Add a repository to track (owner/repo format)").requiredOption("--project-number <n>", "GitHub project number").requiredOption("--status-field-id <id>", "Project status field ID").requiredOption(
7600
+ config.command("repos:add [name]").description("Add a repository to track (interactive wizard, or pass flags for scripted use)").option("--project-number <n>", "GitHub project number (skips interactive prompt)").option("--status-field-id <id>", "Project status field ID (skips interactive prompt)").option(
7459
7601
  "--completion-type <type>",
7460
7602
  "Completion action: addLabel, updateProjectStatus, closeIssue"
7461
- ).option("--completion-option-id <id>", "Option ID for updateProjectStatus").option("--completion-label <label>", "Label for addLabel").action((name, opts) => {
7603
+ ).option("--completion-option-id <id>", "Option ID for updateProjectStatus").option("--completion-label <label>", "Label for addLabel").action(async (name, opts) => {
7604
+ if (!(opts.projectNumber && opts.statusFieldId)) {
7605
+ await runReposAdd(name);
7606
+ return;
7607
+ }
7608
+ if (!name) {
7609
+ console.error("Name argument required in non-interactive mode.");
7610
+ process.exit(1);
7611
+ }
7462
7612
  if (!validateRepoName(name)) {
7463
7613
  console.error("Invalid repo name. Use owner/repo format (e.g., myorg/myrepo)");
7464
7614
  process.exit(1);
@@ -7469,6 +7619,10 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7469
7619
  process.exit(1);
7470
7620
  }
7471
7621
  const shortName2 = name.split("/")[1] ?? name;
7622
+ if (!opts.completionType) {
7623
+ console.error("--completion-type required in non-interactive mode");
7624
+ process.exit(1);
7625
+ }
7472
7626
  let completionAction;
7473
7627
  switch (opts.completionType) {
7474
7628
  case "addLabel":