@ondrej-svec/hog 1.1.2 → 1.2.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
@@ -164,13 +164,6 @@ function getAuth() {
164
164
  return null;
165
165
  }
166
166
  }
167
- function saveAuth(data) {
168
- ensureDir();
169
- writeFileSync(AUTH_FILE, `${JSON.stringify(data, null, 2)}
170
- `, {
171
- mode: 384
172
- });
173
- }
174
167
  function getConfig() {
175
168
  if (!existsSync(CONFIG_FILE)) return {};
176
169
  try {
@@ -3923,78 +3916,10 @@ init_config();
3923
3916
  import { Command } from "commander";
3924
3917
 
3925
3918
  // src/init.ts
3919
+ init_config();
3926
3920
  import { execFileSync } from "child_process";
3927
3921
  import { existsSync as existsSync2 } from "fs";
3928
3922
  import { checkbox, confirm, input, select } from "@inquirer/prompts";
3929
-
3930
- // src/auth.ts
3931
- import { createServer } from "http";
3932
- var AUTH_URL = "https://ticktick.com/oauth/authorize";
3933
- var TOKEN_URL = "https://ticktick.com/oauth/token";
3934
- var REDIRECT_URI = "http://localhost:8080";
3935
- var SCOPE = "tasks:write tasks:read";
3936
- function getAuthorizationUrl(clientId) {
3937
- const params = new URLSearchParams({
3938
- scope: SCOPE,
3939
- client_id: clientId,
3940
- state: "hog",
3941
- redirect_uri: REDIRECT_URI,
3942
- response_type: "code"
3943
- });
3944
- return `${AUTH_URL}?${params}`;
3945
- }
3946
- async function waitForAuthCode() {
3947
- return new Promise((resolve, reject) => {
3948
- const server = createServer((req, res) => {
3949
- const url = new URL(req.url ?? "", REDIRECT_URI);
3950
- const code = url.searchParams.get("code");
3951
- if (code) {
3952
- res.writeHead(200, { "Content-Type": "text/html" });
3953
- res.end(
3954
- "<html><body><h1>Heart of Gold authenticated!</h1><p>You can close this window.</p></body></html>"
3955
- );
3956
- server.close();
3957
- resolve(code);
3958
- } else {
3959
- res.writeHead(400, { "Content-Type": "text/plain" });
3960
- res.end("Missing authorization code");
3961
- server.close();
3962
- reject(new Error("No authorization code received"));
3963
- }
3964
- });
3965
- server.listen(8080, () => {
3966
- });
3967
- server.on("error", reject);
3968
- setTimeout(() => {
3969
- server.close();
3970
- reject(new Error("Authorization timed out (2 min)"));
3971
- }, 12e4);
3972
- });
3973
- }
3974
- async function exchangeCodeForToken(clientId, clientSecret, code) {
3975
- const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
3976
- const res = await fetch(TOKEN_URL, {
3977
- method: "POST",
3978
- headers: {
3979
- Authorization: `Basic ${credentials}`,
3980
- "Content-Type": "application/x-www-form-urlencoded"
3981
- },
3982
- body: new URLSearchParams({
3983
- grant_type: "authorization_code",
3984
- code,
3985
- redirect_uri: REDIRECT_URI
3986
- })
3987
- });
3988
- if (!res.ok) {
3989
- const text = await res.text();
3990
- throw new Error(`Token exchange failed: ${text}`);
3991
- }
3992
- const data = await res.json();
3993
- return data.access_token;
3994
- }
3995
-
3996
- // src/init.ts
3997
- init_config();
3998
3923
  function ghJson(args) {
3999
3924
  const output = execFileSync("gh", args, { encoding: "utf-8", timeout: 3e4 }).trim();
4000
3925
  return JSON.parse(output);
@@ -4049,14 +3974,22 @@ function listAllRepos() {
4049
3974
  }
4050
3975
  function listOrgProjects(owner) {
4051
3976
  try {
4052
- return ghJson(["project", "list", "--owner", owner, "--format", "json"]);
3977
+ const result = ghJson([
3978
+ "project",
3979
+ "list",
3980
+ "--owner",
3981
+ owner,
3982
+ "--format",
3983
+ "json"
3984
+ ]);
3985
+ return result.projects ?? [];
4053
3986
  } catch {
4054
3987
  return [];
4055
3988
  }
4056
3989
  }
4057
3990
  function listProjectFields(owner, projectNumber) {
4058
3991
  try {
4059
- return ghJson([
3992
+ const result = ghJson([
4060
3993
  "project",
4061
3994
  "field-list",
4062
3995
  String(projectNumber),
@@ -4065,16 +3998,18 @@ function listProjectFields(owner, projectNumber) {
4065
3998
  "--format",
4066
3999
  "json"
4067
4000
  ]);
4001
+ return result.fields ?? [];
4068
4002
  } catch {
4069
4003
  return [];
4070
4004
  }
4071
4005
  }
4072
- function detectStatusFieldId(owner, projectNumber) {
4006
+ function detectStatusField(owner, projectNumber) {
4073
4007
  const fields = listProjectFields(owner, projectNumber);
4074
4008
  const statusField = fields.find(
4075
4009
  (f) => f.name === "Status" && f.type === "ProjectV2SingleSelectField"
4076
4010
  );
4077
- return statusField?.id ?? null;
4011
+ if (!statusField) return null;
4012
+ return { fieldId: statusField.id, options: statusField.options ?? [] };
4078
4013
  }
4079
4014
  async function runInit(opts = {}) {
4080
4015
  try {
@@ -4148,8 +4083,10 @@ Configuring ${repoName}...`);
4148
4083
  });
4149
4084
  }
4150
4085
  console.log(" Detecting status field...");
4151
- let statusFieldId = detectStatusFieldId(owner, projectNumber);
4152
- if (statusFieldId) {
4086
+ const statusInfo = detectStatusField(owner, projectNumber);
4087
+ let statusFieldId;
4088
+ if (statusInfo) {
4089
+ statusFieldId = statusInfo.fieldId;
4153
4090
  console.log(` Found status field: ${statusFieldId}`);
4154
4091
  } else {
4155
4092
  console.log(" Could not auto-detect status field.");
@@ -4158,7 +4095,7 @@ Configuring ${repoName}...`);
4158
4095
  });
4159
4096
  }
4160
4097
  const completionType = await select({
4161
- message: ` When a task is completed in TickTick, what should happen on GitHub?`,
4098
+ message: ` When a task is completed, what should happen on GitHub?`,
4162
4099
  choices: [
4163
4100
  { name: "Close the issue", value: "closeIssue" },
4164
4101
  { name: "Add a label (e.g. review:pending)", value: "addLabel" },
@@ -4173,9 +4110,21 @@ Configuring ${repoName}...`);
4173
4110
  });
4174
4111
  completionAction = { type: "addLabel", label };
4175
4112
  } else if (completionType === "updateProjectStatus") {
4176
- const optionId = await input({
4177
- message: " Status option ID to set:"
4178
- });
4113
+ const statusOptions = statusInfo?.options ?? [];
4114
+ let optionId;
4115
+ if (statusOptions.length > 0) {
4116
+ optionId = await select({
4117
+ message: " Status to set when completed:",
4118
+ choices: statusOptions.map((o) => ({
4119
+ name: o.name,
4120
+ value: o.id
4121
+ }))
4122
+ });
4123
+ } else {
4124
+ optionId = await input({
4125
+ message: " Status option ID to set:"
4126
+ });
4127
+ }
4179
4128
  completionAction = { type: "updateProjectStatus", optionId };
4180
4129
  } else {
4181
4130
  completionAction = { type: "closeIssue" };
@@ -4192,43 +4141,11 @@ Configuring ${repoName}...`);
4192
4141
  completionAction
4193
4142
  });
4194
4143
  }
4195
- const enableTickTick = await confirm({
4196
- message: "Enable TickTick integration?",
4197
- default: false
4198
- });
4144
+ const ticktickAlreadyEnabled = existsSync2(`${CONFIG_DIR}/auth.json`);
4199
4145
  let ticktickAuth = false;
4200
- if (enableTickTick) {
4201
- const hasAuth = existsSync2(`${CONFIG_DIR}/auth.json`);
4202
- if (hasAuth) {
4203
- console.log(" TickTick auth already configured.");
4204
- ticktickAuth = true;
4205
- } else {
4206
- const setupNow = await confirm({
4207
- message: " Set up TickTick OAuth now?",
4208
- default: true
4209
- });
4210
- if (setupNow) {
4211
- const clientId = await input({ message: " TickTick OAuth client ID:" });
4212
- const clientSecret = await input({ message: " TickTick OAuth client secret:" });
4213
- const url = getAuthorizationUrl(clientId);
4214
- console.log(`
4215
- Open this URL to authorize:
4216
-
4217
- ${url}
4218
- `);
4219
- try {
4220
- const { exec } = await import("child_process");
4221
- exec(`open "${url}"`);
4222
- } catch {
4223
- }
4224
- console.log(" Waiting for authorization...");
4225
- const code = await waitForAuthCode();
4226
- const accessToken = await exchangeCodeForToken(clientId, clientSecret, code);
4227
- saveAuth({ accessToken, clientId, clientSecret });
4228
- console.log(" TickTick authenticated successfully.");
4229
- ticktickAuth = true;
4230
- }
4231
- }
4146
+ if (ticktickAlreadyEnabled) {
4147
+ ticktickAuth = true;
4148
+ console.log("TickTick auth found \u2014 integration enabled.");
4232
4149
  }
4233
4150
  console.log("\nBoard settings:");
4234
4151
  const refreshInterval = await input({
@@ -4255,7 +4172,7 @@ Configuring ${repoName}...`);
4255
4172
  assignee: login,
4256
4173
  focusDuration: Number.parseInt(focusDuration, 10) || 1500
4257
4174
  },
4258
- ticktick: { enabled: enableTickTick && ticktickAuth },
4175
+ ticktick: { enabled: ticktickAuth },
4259
4176
  profiles: existingConfig?.profiles ?? {}
4260
4177
  };
4261
4178
  saveFullConfig(config2);
@@ -4665,7 +4582,7 @@ function resolveProjectId(projectId) {
4665
4582
  process.exit(1);
4666
4583
  }
4667
4584
  var program = new Command();
4668
- program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.1.2").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
4585
+ program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.2.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
4669
4586
  const opts = thisCommand.opts();
4670
4587
  if (opts.json) setFormat("json");
4671
4588
  if (opts.human) setFormat("human");