@ondrej-svec/hog 1.5.0 → 1.6.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 +181 -26
- package/dist/cli.js.map +1 -1
- package/dist/fetch-worker.js +4 -2
- package/dist/fetch-worker.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -163,6 +163,7 @@ var init_config = __esm({
|
|
|
163
163
|
shortName: z.string().min(1),
|
|
164
164
|
projectNumber: z.number().int().positive(),
|
|
165
165
|
statusFieldId: z.string().min(1),
|
|
166
|
+
dueDateFieldId: z.string().optional(),
|
|
166
167
|
completionAction: COMPLETION_ACTION_SCHEMA,
|
|
167
168
|
statusGroups: z.array(z.string()).optional()
|
|
168
169
|
});
|
|
@@ -532,7 +533,7 @@ function fetchProjectFields(repo, issueNumber, projectNumber) {
|
|
|
532
533
|
const fieldValues = projectItem.fieldValues?.nodes ?? [];
|
|
533
534
|
for (const fv of fieldValues) {
|
|
534
535
|
if (!fv) continue;
|
|
535
|
-
if ("date" in fv && fv.field?.name
|
|
536
|
+
if ("date" in fv && DATE_FIELD_NAME_RE2.test(fv.field?.name ?? "")) {
|
|
536
537
|
fields.targetDate = fv.date;
|
|
537
538
|
}
|
|
538
539
|
if ("name" in fv && fv.field?.name === "Status") {
|
|
@@ -594,7 +595,7 @@ function fetchProjectEnrichment(repo, projectNumber) {
|
|
|
594
595
|
const fieldValues = item.fieldValues?.nodes ?? [];
|
|
595
596
|
for (const fv of fieldValues) {
|
|
596
597
|
if (!fv) continue;
|
|
597
|
-
if ("date" in fv && fv.field?.name
|
|
598
|
+
if ("date" in fv && fv.date && DATE_FIELD_NAME_RE2.test(fv.field?.name ?? "")) {
|
|
598
599
|
enrichment.targetDate = fv.date;
|
|
599
600
|
}
|
|
600
601
|
if ("name" in fv && fv.field?.name === "Status" && fv.name) {
|
|
@@ -824,11 +825,91 @@ async function updateProjectItemStatusAsync(repo, issueNumber, projectConfig) {
|
|
|
824
825
|
`optionId=${optionId}`
|
|
825
826
|
]);
|
|
826
827
|
}
|
|
827
|
-
|
|
828
|
+
async function updateProjectItemDateAsync(repo, issueNumber, projectConfig, dueDate) {
|
|
829
|
+
const [owner, repoName] = repo.split("/");
|
|
830
|
+
const findItemQuery = `
|
|
831
|
+
query($owner: String!, $repo: String!, $issueNumber: Int!) {
|
|
832
|
+
repository(owner: $owner, name: $repo) {
|
|
833
|
+
issue(number: $issueNumber) {
|
|
834
|
+
projectItems(first: 10) {
|
|
835
|
+
nodes {
|
|
836
|
+
id
|
|
837
|
+
project { number }
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
`;
|
|
844
|
+
const findResult = await runGhJsonAsync([
|
|
845
|
+
"api",
|
|
846
|
+
"graphql",
|
|
847
|
+
"-f",
|
|
848
|
+
`query=${findItemQuery}`,
|
|
849
|
+
"-F",
|
|
850
|
+
`owner=${owner}`,
|
|
851
|
+
"-F",
|
|
852
|
+
`repo=${repoName}`,
|
|
853
|
+
"-F",
|
|
854
|
+
`issueNumber=${String(issueNumber)}`
|
|
855
|
+
]);
|
|
856
|
+
const items = findResult?.data?.repository?.issue?.projectItems?.nodes ?? [];
|
|
857
|
+
const projectItem = items.find((item) => item?.project?.number === projectConfig.projectNumber);
|
|
858
|
+
if (!projectItem?.id) return;
|
|
859
|
+
const projectQuery = `
|
|
860
|
+
query($owner: String!) {
|
|
861
|
+
organization(login: $owner) {
|
|
862
|
+
projectV2(number: ${projectConfig.projectNumber}) {
|
|
863
|
+
id
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
`;
|
|
868
|
+
const projectResult = await runGhJsonAsync([
|
|
869
|
+
"api",
|
|
870
|
+
"graphql",
|
|
871
|
+
"-f",
|
|
872
|
+
`query=${projectQuery}`,
|
|
873
|
+
"-F",
|
|
874
|
+
`owner=${owner}`
|
|
875
|
+
]);
|
|
876
|
+
const projectId = projectResult?.data?.organization?.projectV2?.id;
|
|
877
|
+
if (!projectId) return;
|
|
878
|
+
const mutation = `
|
|
879
|
+
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $date: Date!) {
|
|
880
|
+
updateProjectV2ItemFieldValue(
|
|
881
|
+
input: {
|
|
882
|
+
projectId: $projectId
|
|
883
|
+
itemId: $itemId
|
|
884
|
+
fieldId: $fieldId
|
|
885
|
+
value: { date: $date }
|
|
886
|
+
}
|
|
887
|
+
) {
|
|
888
|
+
projectV2Item { id }
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
`;
|
|
892
|
+
await runGhAsync([
|
|
893
|
+
"api",
|
|
894
|
+
"graphql",
|
|
895
|
+
"-f",
|
|
896
|
+
`query=${mutation}`,
|
|
897
|
+
"-F",
|
|
898
|
+
`projectId=${projectId}`,
|
|
899
|
+
"-F",
|
|
900
|
+
`itemId=${projectItem.id}`,
|
|
901
|
+
"-F",
|
|
902
|
+
`fieldId=${projectConfig.dueDateFieldId}`,
|
|
903
|
+
"-F",
|
|
904
|
+
`date=${dueDate}`
|
|
905
|
+
]);
|
|
906
|
+
}
|
|
907
|
+
var execFileAsync, DATE_FIELD_NAME_RE2;
|
|
828
908
|
var init_github = __esm({
|
|
829
909
|
"src/github.ts"() {
|
|
830
910
|
"use strict";
|
|
831
911
|
execFileAsync = promisify(execFile);
|
|
912
|
+
DATE_FIELD_NAME_RE2 = /^(target\s*date|due\s*date|due|deadline)$/i;
|
|
832
913
|
}
|
|
833
914
|
});
|
|
834
915
|
|
|
@@ -1222,8 +1303,16 @@ function useActions({
|
|
|
1222
1303
|
});
|
|
1223
1304
|
}, [toast, refresh]);
|
|
1224
1305
|
const handleCreateIssue = useCallback(
|
|
1225
|
-
async (repo, title, body, labels) => {
|
|
1226
|
-
const
|
|
1306
|
+
async (repo, title, body, dueDate, labels) => {
|
|
1307
|
+
const repoConfig = configRef.current.repos.find((r) => r.name === repo);
|
|
1308
|
+
let effectiveBody = body;
|
|
1309
|
+
if (dueDate && !repoConfig?.dueDateFieldId) {
|
|
1310
|
+
const dueLine = `Due: ${dueDate}`;
|
|
1311
|
+
effectiveBody = body ? `${body}
|
|
1312
|
+
|
|
1313
|
+
${dueLine}` : dueLine;
|
|
1314
|
+
}
|
|
1315
|
+
const args = ["issue", "create", "--repo", repo, "--title", title, "--body", effectiveBody];
|
|
1227
1316
|
if (labels && labels.length > 0) {
|
|
1228
1317
|
for (const label of labels) {
|
|
1229
1318
|
args.push("--label", label);
|
|
@@ -1235,7 +1324,15 @@ function useActions({
|
|
|
1235
1324
|
const output = stdout.trim();
|
|
1236
1325
|
const match = output.match(/\/(\d+)$/);
|
|
1237
1326
|
const issueNumber = match?.[1] ? parseInt(match[1], 10) : 0;
|
|
1238
|
-
const shortName =
|
|
1327
|
+
const shortName = repoConfig?.shortName ?? repo;
|
|
1328
|
+
if (issueNumber > 0 && dueDate && repoConfig?.dueDateFieldId) {
|
|
1329
|
+
const dueDateConfig = {
|
|
1330
|
+
projectNumber: repoConfig.projectNumber,
|
|
1331
|
+
dueDateFieldId: repoConfig.dueDateFieldId
|
|
1332
|
+
};
|
|
1333
|
+
updateProjectItemDateAsync(repo, issueNumber, dueDateConfig, dueDate).catch(() => {
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1239
1336
|
t.resolve(`Created ${shortName}#${issueNumber}`);
|
|
1240
1337
|
refresh();
|
|
1241
1338
|
onOverlayDone();
|
|
@@ -2778,13 +2875,19 @@ function CreateIssueForm({
|
|
|
2778
2875
|
currentLabels: [],
|
|
2779
2876
|
labelCache: labelCache ?? {},
|
|
2780
2877
|
onConfirm: (addLabels) => {
|
|
2781
|
-
onSubmit(
|
|
2878
|
+
onSubmit(
|
|
2879
|
+
selectedRepo.name,
|
|
2880
|
+
title,
|
|
2881
|
+
"",
|
|
2882
|
+
null,
|
|
2883
|
+
addLabels.length > 0 ? addLabels : void 0
|
|
2884
|
+
);
|
|
2782
2885
|
},
|
|
2783
2886
|
onCancel: () => {
|
|
2784
|
-
onSubmit(selectedRepo.name, title, "");
|
|
2887
|
+
onSubmit(selectedRepo.name, title, "", null);
|
|
2785
2888
|
},
|
|
2786
2889
|
onError: () => {
|
|
2787
|
-
onSubmit(selectedRepo.name, title, "");
|
|
2890
|
+
onSubmit(selectedRepo.name, title, "", null);
|
|
2788
2891
|
}
|
|
2789
2892
|
}
|
|
2790
2893
|
)
|
|
@@ -2820,7 +2923,7 @@ function CreateIssueForm({
|
|
|
2820
2923
|
setTitle(trimmed);
|
|
2821
2924
|
setField("labels");
|
|
2822
2925
|
} else {
|
|
2823
|
-
onSubmit(selectedRepo.name, trimmed, "");
|
|
2926
|
+
onSubmit(selectedRepo.name, trimmed, "", null);
|
|
2824
2927
|
}
|
|
2825
2928
|
}
|
|
2826
2929
|
}
|
|
@@ -3109,7 +3212,7 @@ function NlCreateOverlay({
|
|
|
3109
3212
|
setBody(content.trimEnd());
|
|
3110
3213
|
} finally {
|
|
3111
3214
|
onResumeRef.current?.();
|
|
3112
|
-
if (tmpFile) {
|
|
3215
|
+
if (tmpFile && tmpDir) {
|
|
3113
3216
|
try {
|
|
3114
3217
|
rmSync2(tmpDir, { recursive: true, force: true });
|
|
3115
3218
|
} catch {
|
|
@@ -3186,6 +3289,7 @@ function NlCreateOverlay({
|
|
|
3186
3289
|
selectedRepo.name,
|
|
3187
3290
|
parsed.title,
|
|
3188
3291
|
text.trim(),
|
|
3292
|
+
parsed.dueDate,
|
|
3189
3293
|
labels.length > 0 ? labels : void 0
|
|
3190
3294
|
);
|
|
3191
3295
|
}
|
|
@@ -3196,7 +3300,7 @@ function NlCreateOverlay({
|
|
|
3196
3300
|
] });
|
|
3197
3301
|
}
|
|
3198
3302
|
if (parsed) {
|
|
3199
|
-
const labels =
|
|
3303
|
+
const labels = [...parsed.labels];
|
|
3200
3304
|
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
3201
3305
|
/* @__PURE__ */ jsx9(Text9, { color: "cyan", bold: true, children: "\u2728 Creating Issue" }),
|
|
3202
3306
|
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
@@ -3223,7 +3327,6 @@ function NlCreateOverlay({
|
|
|
3223
3327
|
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Due: " }),
|
|
3224
3328
|
/* @__PURE__ */ jsx9(Text9, { children: formatDue(parsed.dueDate) })
|
|
3225
3329
|
] }) : null,
|
|
3226
|
-
parsed.dueDate && selectedRepo && !hasDueLabelInCache(labelCache, selectedRepo.name) ? /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u26A0 No due:* label in this repo \u2014 will try to create label on submit" }) : null,
|
|
3227
3330
|
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Enter:add body Esc:cancel" })
|
|
3228
3331
|
] });
|
|
3229
3332
|
}
|
|
@@ -3245,19 +3348,11 @@ function NlCreateOverlay({
|
|
|
3245
3348
|
] });
|
|
3246
3349
|
}
|
|
3247
3350
|
function buildLabelList(parsed) {
|
|
3248
|
-
|
|
3249
|
-
if (parsed.dueDate) {
|
|
3250
|
-
labels.push(`due:${parsed.dueDate}`);
|
|
3251
|
-
}
|
|
3252
|
-
return labels;
|
|
3253
|
-
}
|
|
3254
|
-
function hasDueLabelInCache(labelCache, repoName) {
|
|
3255
|
-
return (labelCache[repoName] ?? []).some((l) => l.name.startsWith("due:"));
|
|
3351
|
+
return [...parsed.labels];
|
|
3256
3352
|
}
|
|
3257
3353
|
function formatDue(dueDate) {
|
|
3258
3354
|
const d = /* @__PURE__ */ new Date(`${dueDate}T12:00:00`);
|
|
3259
|
-
|
|
3260
|
-
return `${human} (label: due:${dueDate})`;
|
|
3355
|
+
return d.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric" });
|
|
3261
3356
|
}
|
|
3262
3357
|
var init_nl_create_overlay = __esm({
|
|
3263
3358
|
"src/board/components/nl-create-overlay.tsx"() {
|
|
@@ -4140,8 +4235,8 @@ function Dashboard({ config: config2, options, activeProfile }) {
|
|
|
4140
4235
|
const pendingPickRef = useRef11(null);
|
|
4141
4236
|
const labelCacheRef = useRef11({});
|
|
4142
4237
|
const handleCreateIssueWithPrompt = useCallback10(
|
|
4143
|
-
(repo, title, body, labels) => {
|
|
4144
|
-
actions.handleCreateIssue(repo, title, body, labels).then((result) => {
|
|
4238
|
+
(repo, title, body, dueDate, labels) => {
|
|
4239
|
+
actions.handleCreateIssue(repo, title, body, dueDate, labels).then((result) => {
|
|
4145
4240
|
if (result) {
|
|
4146
4241
|
pendingPickRef.current = result;
|
|
4147
4242
|
ui.enterConfirmPick();
|
|
@@ -5057,6 +5152,34 @@ function detectStatusField(owner, projectNumber) {
|
|
|
5057
5152
|
if (!statusField) return null;
|
|
5058
5153
|
return { fieldId: statusField.id, options: statusField.options ?? [] };
|
|
5059
5154
|
}
|
|
5155
|
+
var DATE_FIELD_NAME_RE = /^(target\s*date|due\s*date|due|deadline)$/i;
|
|
5156
|
+
function detectDateField(owner, projectNumber) {
|
|
5157
|
+
const fields = listProjectFields(owner, projectNumber);
|
|
5158
|
+
return fields.find((f) => DATE_FIELD_NAME_RE.test(f.name)) ?? null;
|
|
5159
|
+
}
|
|
5160
|
+
function createDateField(owner, projectNumber, fieldName) {
|
|
5161
|
+
try {
|
|
5162
|
+
execFileSync(
|
|
5163
|
+
"gh",
|
|
5164
|
+
[
|
|
5165
|
+
"project",
|
|
5166
|
+
"field-create",
|
|
5167
|
+
String(projectNumber),
|
|
5168
|
+
"--owner",
|
|
5169
|
+
owner,
|
|
5170
|
+
"--name",
|
|
5171
|
+
fieldName,
|
|
5172
|
+
"--data-type",
|
|
5173
|
+
"DATE"
|
|
5174
|
+
],
|
|
5175
|
+
{ encoding: "utf-8", timeout: 3e4 }
|
|
5176
|
+
);
|
|
5177
|
+
const fields = listProjectFields(owner, projectNumber);
|
|
5178
|
+
return fields.find((f) => f.name === fieldName)?.id ?? null;
|
|
5179
|
+
} catch {
|
|
5180
|
+
return null;
|
|
5181
|
+
}
|
|
5182
|
+
}
|
|
5060
5183
|
async function runInit(opts = {}) {
|
|
5061
5184
|
try {
|
|
5062
5185
|
await runWizard(opts);
|
|
@@ -5140,6 +5263,37 @@ Configuring ${repoName}...`);
|
|
|
5140
5263
|
message: " Enter status field ID manually:"
|
|
5141
5264
|
});
|
|
5142
5265
|
}
|
|
5266
|
+
console.log(" Detecting due date field...");
|
|
5267
|
+
let dueDateFieldId;
|
|
5268
|
+
const existingDateField = detectDateField(owner, projectNumber);
|
|
5269
|
+
if (existingDateField) {
|
|
5270
|
+
console.log(` Found date field: "${existingDateField.name}" (${existingDateField.id})`);
|
|
5271
|
+
const useDateField = await confirm({
|
|
5272
|
+
message: ` Use "${existingDateField.name}" for due dates?`,
|
|
5273
|
+
default: true
|
|
5274
|
+
});
|
|
5275
|
+
if (useDateField) {
|
|
5276
|
+
dueDateFieldId = existingDateField.id;
|
|
5277
|
+
}
|
|
5278
|
+
} else {
|
|
5279
|
+
console.log(" No due date field found in this project.");
|
|
5280
|
+
const createField = await confirm({
|
|
5281
|
+
message: ' Create a "Due Date" field for due dates?',
|
|
5282
|
+
default: false
|
|
5283
|
+
});
|
|
5284
|
+
if (createField) {
|
|
5285
|
+
console.log(' Creating "Due Date" field...');
|
|
5286
|
+
const newFieldId = createDateField(owner, projectNumber, "Due Date");
|
|
5287
|
+
if (newFieldId) {
|
|
5288
|
+
dueDateFieldId = newFieldId;
|
|
5289
|
+
console.log(` Created "Due Date" field (${newFieldId})`);
|
|
5290
|
+
} else {
|
|
5291
|
+
console.log(" Could not create field \u2014 due dates will be stored in issue body.");
|
|
5292
|
+
}
|
|
5293
|
+
} else {
|
|
5294
|
+
console.log(" Skipped \u2014 due dates will be stored in issue body.");
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5143
5297
|
const completionType = await select({
|
|
5144
5298
|
message: ` When a task is completed, what should happen on GitHub?`,
|
|
5145
5299
|
choices: [
|
|
@@ -5184,6 +5338,7 @@ Configuring ${repoName}...`);
|
|
|
5184
5338
|
shortName,
|
|
5185
5339
|
projectNumber,
|
|
5186
5340
|
statusFieldId,
|
|
5341
|
+
...dueDateFieldId ? { dueDateFieldId } : {},
|
|
5187
5342
|
completionAction
|
|
5188
5343
|
});
|
|
5189
5344
|
}
|
|
@@ -5650,7 +5805,7 @@ function resolveProjectId(projectId) {
|
|
|
5650
5805
|
process.exit(1);
|
|
5651
5806
|
}
|
|
5652
5807
|
var program = new Command();
|
|
5653
|
-
program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.
|
|
5808
|
+
program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.6.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
|
|
5654
5809
|
const opts = thisCommand.opts();
|
|
5655
5810
|
if (opts.json) setFormat("json");
|
|
5656
5811
|
if (opts.human) setFormat("human");
|