@ondrej-svec/hog 1.15.0 → 1.16.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
@@ -2306,7 +2306,8 @@ function useKeyboard({
2306
2306
  handleEnterFuzzyPicker,
2307
2307
  handleEnterEditIssue,
2308
2308
  handleUndo,
2309
- handleToggleLog
2309
+ handleToggleLog,
2310
+ showDetailPanel
2310
2311
  ]
2311
2312
  );
2312
2313
  const inputActive = ui.state.mode === "normal" || ui.state.mode === "multiSelect" || ui.state.mode === "focus" || ui.state.mode === "overlay:detail";
@@ -6850,6 +6851,145 @@ Config written to ${CONFIG_DIR}/config.json`);
6850
6851
  console.log(" hog task list # List TickTick tasks");
6851
6852
  console.log(" hog config show # View configuration\n");
6852
6853
  }
6854
+ async function runReposAdd(initialRepoName) {
6855
+ try {
6856
+ await runReposAddWizard(initialRepoName);
6857
+ } catch (error) {
6858
+ if (error instanceof Error && error.message.includes("User force closed")) {
6859
+ console.log("\nCancelled. No changes were made.");
6860
+ return;
6861
+ }
6862
+ throw error;
6863
+ }
6864
+ }
6865
+ async function runReposAddWizard(initialRepoName) {
6866
+ console.log("\n\u{1F417} hog config repos:add\n");
6867
+ const cfg = loadFullConfig();
6868
+ let repoName = initialRepoName;
6869
+ if (!repoName) {
6870
+ console.log("Fetching repositories...");
6871
+ const allRepos = listAllRepos();
6872
+ const configuredNames = new Set(cfg.repos.map((r) => r.name));
6873
+ const available = allRepos.filter((r) => !configuredNames.has(r.nameWithOwner));
6874
+ if (available.length === 0) {
6875
+ console.log(
6876
+ "No more repositories available to add. All accessible repos are already tracked."
6877
+ );
6878
+ return;
6879
+ }
6880
+ repoName = await select({
6881
+ message: "Select repository to add:",
6882
+ choices: available.map((r) => ({ name: r.nameWithOwner, value: r.nameWithOwner }))
6883
+ });
6884
+ }
6885
+ if (!validateRepoName(repoName)) {
6886
+ console.error("Invalid repo name. Use owner/repo format (e.g. myorg/myrepo).");
6887
+ process.exit(1);
6888
+ }
6889
+ if (findRepo(cfg, repoName)) {
6890
+ console.error(`Repo "${repoName}" is already configured.`);
6891
+ process.exit(1);
6892
+ }
6893
+ const [owner, name] = repoName.split("/");
6894
+ console.log(`
6895
+ Configuring ${repoName}...`);
6896
+ console.log(" Fetching GitHub Projects...");
6897
+ const projects = listOrgProjects(owner);
6898
+ let projectNumber;
6899
+ if (projects.length === 0) {
6900
+ console.log(" No GitHub Projects found. Enter project number manually.");
6901
+ const num = await input({ message: ` Project number for ${repoName}:` });
6902
+ projectNumber = Number.parseInt(num, 10);
6903
+ } else {
6904
+ projectNumber = await select({
6905
+ message: ` Select project for ${repoName}:`,
6906
+ choices: projects.map((p) => ({ name: `#${p.number} \u2014 ${p.title}`, value: p.number }))
6907
+ });
6908
+ }
6909
+ console.log(" Detecting status field...");
6910
+ const statusInfo = detectStatusField(owner, projectNumber);
6911
+ let statusFieldId;
6912
+ if (statusInfo) {
6913
+ statusFieldId = statusInfo.fieldId;
6914
+ console.log(` Found status field: ${statusFieldId}`);
6915
+ } else {
6916
+ console.log(" Could not auto-detect status field.");
6917
+ statusFieldId = await input({ message: " Enter status field ID manually:" });
6918
+ }
6919
+ console.log(" Detecting due date field...");
6920
+ let dueDateFieldId;
6921
+ const existingDateField = detectDateField(owner, projectNumber);
6922
+ if (existingDateField) {
6923
+ console.log(` Found date field: "${existingDateField.name}" (${existingDateField.id})`);
6924
+ const useDateField = await confirm({
6925
+ message: ` Use "${existingDateField.name}" for due dates?`,
6926
+ default: true
6927
+ });
6928
+ if (useDateField) {
6929
+ dueDateFieldId = existingDateField.id;
6930
+ }
6931
+ } else {
6932
+ console.log(" No due date field found.");
6933
+ const createField = await confirm({
6934
+ message: ' Create a "Due Date" field for this project?',
6935
+ default: false
6936
+ });
6937
+ if (createField) {
6938
+ console.log(' Creating "Due Date" field...');
6939
+ const newFieldId = createDateField(owner, projectNumber, "Due Date");
6940
+ if (newFieldId) {
6941
+ dueDateFieldId = newFieldId;
6942
+ console.log(` Created "Due Date" field (${newFieldId})`);
6943
+ } else {
6944
+ console.log(" Could not create field \u2014 due dates will be stored in issue body.");
6945
+ }
6946
+ }
6947
+ }
6948
+ const completionType = await select({
6949
+ message: " When a task is completed, what should happen on GitHub?",
6950
+ choices: [
6951
+ { name: "Close the issue", value: "closeIssue" },
6952
+ { name: "Add a label (e.g. review:pending)", value: "addLabel" },
6953
+ { name: "Update project status column", value: "updateProjectStatus" }
6954
+ ]
6955
+ });
6956
+ let completionAction;
6957
+ if (completionType === "addLabel") {
6958
+ const label = await input({ message: " Label to add:", default: "review:pending" });
6959
+ completionAction = { type: "addLabel", label };
6960
+ } else if (completionType === "updateProjectStatus") {
6961
+ const statusOptions = statusInfo?.options ?? [];
6962
+ let optionId;
6963
+ if (statusOptions.length > 0) {
6964
+ optionId = await select({
6965
+ message: " Status to set when completed:",
6966
+ choices: statusOptions.map((o) => ({ name: o.name, value: o.id }))
6967
+ });
6968
+ } else {
6969
+ optionId = await input({ message: " Status option ID to set:" });
6970
+ }
6971
+ completionAction = { type: "updateProjectStatus", optionId };
6972
+ } else {
6973
+ completionAction = { type: "closeIssue" };
6974
+ }
6975
+ const shortName2 = await input({
6976
+ message: ` Short name for ${repoName}:`,
6977
+ default: name
6978
+ });
6979
+ const newRepo = {
6980
+ name: repoName,
6981
+ shortName: shortName2,
6982
+ projectNumber,
6983
+ statusFieldId,
6984
+ ...dueDateFieldId ? { dueDateFieldId } : {},
6985
+ completionAction
6986
+ };
6987
+ cfg.repos.push(newRepo);
6988
+ saveFullConfig(cfg);
6989
+ console.log(`
6990
+ Added ${shortName2} \u2192 ${repoName}`);
6991
+ console.log(" Run: hog board --live\n");
6992
+ }
6853
6993
 
6854
6994
  // src/cli.ts
6855
6995
  init_log_persistence();
@@ -7273,7 +7413,7 @@ function resolveProjectId(projectId) {
7273
7413
  process.exit(1);
7274
7414
  }
7275
7415
  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) => {
7416
+ program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.16.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
7277
7417
  const opts = thisCommand.opts();
7278
7418
  if (opts.json) setFormat("json");
7279
7419
  if (opts.human) setFormat("human");
@@ -7455,10 +7595,18 @@ config.command("repos").description("List configured repositories").action(() =>
7455
7595
  }
7456
7596
  }
7457
7597
  });
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(
7598
+ 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
7599
  "--completion-type <type>",
7460
7600
  "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) => {
7601
+ ).option("--completion-option-id <id>", "Option ID for updateProjectStatus").option("--completion-label <label>", "Label for addLabel").action(async (name, opts) => {
7602
+ if (!(opts.projectNumber && opts.statusFieldId)) {
7603
+ await runReposAdd(name);
7604
+ return;
7605
+ }
7606
+ if (!name) {
7607
+ console.error("Name argument required in non-interactive mode.");
7608
+ process.exit(1);
7609
+ }
7462
7610
  if (!validateRepoName(name)) {
7463
7611
  console.error("Invalid repo name. Use owner/repo format (e.g., myorg/myrepo)");
7464
7612
  process.exit(1);
@@ -7469,6 +7617,10 @@ config.command("repos:add <name>").description("Add a repository to track (owner
7469
7617
  process.exit(1);
7470
7618
  }
7471
7619
  const shortName2 = name.split("/")[1] ?? name;
7620
+ if (!opts.completionType) {
7621
+ console.error("--completion-type required in non-interactive mode");
7622
+ process.exit(1);
7623
+ }
7472
7624
  let completionAction;
7473
7625
  switch (opts.completionType) {
7474
7626
  case "addLabel":