@ondrej-svec/hog 1.20.0 → 1.21.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 +3432 -1808
- package/dist/cli.js.map +1 -1
- package/dist/fetch-worker.js +145 -243
- package/dist/fetch-worker.js.map +1 -1
- package/package.json +1 -1
package/dist/fetch-worker.js
CHANGED
|
@@ -8,172 +8,6 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/api.ts
|
|
12
|
-
var BASE_URL, TickTickClient;
|
|
13
|
-
var init_api = __esm({
|
|
14
|
-
"src/api.ts"() {
|
|
15
|
-
"use strict";
|
|
16
|
-
BASE_URL = "https://api.ticktick.com/open/v1";
|
|
17
|
-
TickTickClient = class {
|
|
18
|
-
token;
|
|
19
|
-
constructor(token) {
|
|
20
|
-
this.token = token;
|
|
21
|
-
}
|
|
22
|
-
async request(method, path, body) {
|
|
23
|
-
const url = `${BASE_URL}${path}`;
|
|
24
|
-
const init = {
|
|
25
|
-
method,
|
|
26
|
-
headers: {
|
|
27
|
-
Authorization: `Bearer ${this.token}`,
|
|
28
|
-
"Content-Type": "application/json"
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
if (body !== void 0) {
|
|
32
|
-
init.body = JSON.stringify(body);
|
|
33
|
-
}
|
|
34
|
-
const res = await fetch(url, init);
|
|
35
|
-
if (!res.ok) {
|
|
36
|
-
const text2 = await res.text();
|
|
37
|
-
throw new Error(`TickTick API error ${res.status}: ${text2}`);
|
|
38
|
-
}
|
|
39
|
-
const text = await res.text();
|
|
40
|
-
if (!text) return null;
|
|
41
|
-
return JSON.parse(text);
|
|
42
|
-
}
|
|
43
|
-
async listProjects() {
|
|
44
|
-
return await this.request("GET", "/project") ?? [];
|
|
45
|
-
}
|
|
46
|
-
async getProject(projectId) {
|
|
47
|
-
const result = await this.request("GET", `/project/${projectId}`);
|
|
48
|
-
if (!result) throw new Error(`TickTick API returned empty response for project ${projectId}`);
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
async getProjectData(projectId) {
|
|
52
|
-
const result = await this.request("GET", `/project/${projectId}/data`);
|
|
53
|
-
if (!result)
|
|
54
|
-
throw new Error(`TickTick API returned empty response for project data ${projectId}`);
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
async listTasks(projectId) {
|
|
58
|
-
const data = await this.getProjectData(projectId);
|
|
59
|
-
return data.tasks ?? [];
|
|
60
|
-
}
|
|
61
|
-
async getTask(projectId, taskId) {
|
|
62
|
-
const result = await this.request("GET", `/project/${projectId}/task/${taskId}`);
|
|
63
|
-
if (!result) throw new Error(`TickTick API returned empty response for task ${taskId}`);
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
async createTask(input) {
|
|
67
|
-
const result = await this.request("POST", "/task", input);
|
|
68
|
-
if (!result) throw new Error("TickTick API returned empty response for createTask");
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
async updateTask(input) {
|
|
72
|
-
const result = await this.request("POST", `/task/${input.id}`, input);
|
|
73
|
-
if (!result) throw new Error(`TickTick API returned empty response for updateTask ${input.id}`);
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
async completeTask(projectId, taskId) {
|
|
77
|
-
await this.request("POST", `/project/${projectId}/task/${taskId}/complete`);
|
|
78
|
-
}
|
|
79
|
-
async deleteTask(projectId, taskId) {
|
|
80
|
-
await this.request("DELETE", `/project/${projectId}/task/${taskId}`);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// src/config.ts
|
|
87
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
88
|
-
import { homedir } from "os";
|
|
89
|
-
import { isAbsolute, join, normalize } from "path";
|
|
90
|
-
import { z } from "zod";
|
|
91
|
-
function getAuth() {
|
|
92
|
-
if (!existsSync(AUTH_FILE)) return null;
|
|
93
|
-
try {
|
|
94
|
-
const raw = JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
95
|
-
const result = AUTH_SCHEMA.safeParse(raw);
|
|
96
|
-
return result.success ? result.data : null;
|
|
97
|
-
} catch {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
function requireAuth() {
|
|
102
|
-
const auth = getAuth();
|
|
103
|
-
if (!auth) {
|
|
104
|
-
console.error("Not authenticated. Run `hog init` first.");
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
return auth;
|
|
108
|
-
}
|
|
109
|
-
var CONFIG_DIR, AUTH_FILE, CONFIG_FILE, AUTH_SCHEMA, COMPLETION_ACTION_SCHEMA, REPO_NAME_PATTERN, CLAUDE_START_COMMAND_SCHEMA, REPO_CONFIG_SCHEMA, BOARD_CONFIG_SCHEMA, TICKTICK_CONFIG_SCHEMA, PROFILE_SCHEMA, HOG_CONFIG_SCHEMA;
|
|
110
|
-
var init_config = __esm({
|
|
111
|
-
"src/config.ts"() {
|
|
112
|
-
"use strict";
|
|
113
|
-
CONFIG_DIR = join(homedir(), ".config", "hog");
|
|
114
|
-
AUTH_FILE = join(CONFIG_DIR, "auth.json");
|
|
115
|
-
CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
116
|
-
AUTH_SCHEMA = z.object({
|
|
117
|
-
accessToken: z.string(),
|
|
118
|
-
clientId: z.string(),
|
|
119
|
-
clientSecret: z.string(),
|
|
120
|
-
openrouterApiKey: z.string().optional()
|
|
121
|
-
});
|
|
122
|
-
COMPLETION_ACTION_SCHEMA = z.discriminatedUnion("type", [
|
|
123
|
-
z.object({ type: z.literal("updateProjectStatus"), optionId: z.string() }),
|
|
124
|
-
z.object({ type: z.literal("closeIssue") }),
|
|
125
|
-
z.object({ type: z.literal("addLabel"), label: z.string() })
|
|
126
|
-
]);
|
|
127
|
-
REPO_NAME_PATTERN = /^[\w.-]+\/[\w.-]+$/;
|
|
128
|
-
CLAUDE_START_COMMAND_SCHEMA = z.object({
|
|
129
|
-
command: z.string().min(1),
|
|
130
|
-
extraArgs: z.array(z.string())
|
|
131
|
-
});
|
|
132
|
-
REPO_CONFIG_SCHEMA = z.object({
|
|
133
|
-
name: z.string().regex(REPO_NAME_PATTERN, "Must be owner/repo format"),
|
|
134
|
-
shortName: z.string().min(1),
|
|
135
|
-
projectNumber: z.number().int().positive(),
|
|
136
|
-
statusFieldId: z.string().min(1),
|
|
137
|
-
dueDateFieldId: z.string().optional(),
|
|
138
|
-
completionAction: COMPLETION_ACTION_SCHEMA,
|
|
139
|
-
statusGroups: z.array(z.string()).optional(),
|
|
140
|
-
localPath: z.string().refine((p) => isAbsolute(p), { message: "localPath must be an absolute path" }).refine((p) => normalize(p) === p, {
|
|
141
|
-
message: "localPath must be normalized (no .. segments)"
|
|
142
|
-
}).refine((p) => !p.includes("\0"), { message: "localPath must not contain null bytes" }).optional(),
|
|
143
|
-
claudeStartCommand: CLAUDE_START_COMMAND_SCHEMA.optional(),
|
|
144
|
-
claudePrompt: z.string().optional()
|
|
145
|
-
});
|
|
146
|
-
BOARD_CONFIG_SCHEMA = z.object({
|
|
147
|
-
refreshInterval: z.number().int().min(10).default(60),
|
|
148
|
-
backlogLimit: z.number().int().min(1).default(20),
|
|
149
|
-
assignee: z.string().min(1),
|
|
150
|
-
focusDuration: z.number().int().min(60).default(1500),
|
|
151
|
-
claudeStartCommand: CLAUDE_START_COMMAND_SCHEMA.optional(),
|
|
152
|
-
claudePrompt: z.string().optional(),
|
|
153
|
-
claudeLaunchMode: z.enum(["auto", "tmux", "terminal"]).optional(),
|
|
154
|
-
claudeTerminalApp: z.enum(["Terminal", "iTerm", "Ghostty", "WezTerm", "Kitty", "Alacritty"]).optional()
|
|
155
|
-
});
|
|
156
|
-
TICKTICK_CONFIG_SCHEMA = z.object({
|
|
157
|
-
enabled: z.boolean().default(true)
|
|
158
|
-
});
|
|
159
|
-
PROFILE_SCHEMA = z.object({
|
|
160
|
-
repos: z.array(REPO_CONFIG_SCHEMA).default([]),
|
|
161
|
-
board: BOARD_CONFIG_SCHEMA,
|
|
162
|
-
ticktick: TICKTICK_CONFIG_SCHEMA.default({ enabled: true })
|
|
163
|
-
});
|
|
164
|
-
HOG_CONFIG_SCHEMA = z.object({
|
|
165
|
-
version: z.number().int().default(3),
|
|
166
|
-
defaultProjectId: z.string().optional(),
|
|
167
|
-
defaultProjectName: z.string().optional(),
|
|
168
|
-
repos: z.array(REPO_CONFIG_SCHEMA).default([]),
|
|
169
|
-
board: BOARD_CONFIG_SCHEMA,
|
|
170
|
-
ticktick: TICKTICK_CONFIG_SCHEMA.default({ enabled: true }),
|
|
171
|
-
profiles: z.record(z.string(), PROFILE_SCHEMA).default({}),
|
|
172
|
-
defaultProfile: z.string().optional()
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
|
|
177
11
|
// src/github.ts
|
|
178
12
|
import { execFile, execFileSync } from "child_process";
|
|
179
13
|
import { promisify } from "util";
|
|
@@ -184,6 +18,23 @@ function runGhJson(args) {
|
|
|
184
18
|
const output = runGh(args);
|
|
185
19
|
return JSON.parse(output);
|
|
186
20
|
}
|
|
21
|
+
function runGhGraphQL(args) {
|
|
22
|
+
try {
|
|
23
|
+
return runGhJson(args);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (err && typeof err === "object" && "stdout" in err) {
|
|
26
|
+
const stdout = err.stdout;
|
|
27
|
+
const output = typeof stdout === "string" ? stdout : stdout?.toString("utf-8");
|
|
28
|
+
if (output) {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(output.trim());
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
187
38
|
function fetchRepoIssues(repo, options2 = {}) {
|
|
188
39
|
const { state = "open", limit = 100 } = options2;
|
|
189
40
|
const args = [
|
|
@@ -206,41 +57,37 @@ function fetchRepoIssues(repo, options2 = {}) {
|
|
|
206
57
|
function fetchProjectEnrichment(repo, projectNumber) {
|
|
207
58
|
const [owner] = repo.split("/");
|
|
208
59
|
if (!owner) return /* @__PURE__ */ new Map();
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
60
|
+
const projectItemsFragment = `
|
|
61
|
+
projectV2(number: $projectNumber) {
|
|
62
|
+
items(first: 100, after: $cursor) {
|
|
63
|
+
pageInfo { hasNextPage endCursor }
|
|
64
|
+
nodes {
|
|
65
|
+
content {
|
|
66
|
+
... on Issue {
|
|
67
|
+
number
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
fieldValues(first: 20) {
|
|
215
71
|
nodes {
|
|
216
|
-
|
|
217
|
-
... on
|
|
218
|
-
|
|
219
|
-
|
|
72
|
+
... on ProjectV2ItemFieldDateValue {
|
|
73
|
+
field { ... on ProjectV2Field { name } }
|
|
74
|
+
date
|
|
75
|
+
}
|
|
76
|
+
... on ProjectV2ItemFieldSingleSelectValue {
|
|
77
|
+
field { ... on ProjectV2SingleSelectField { name } }
|
|
78
|
+
name
|
|
79
|
+
}
|
|
80
|
+
... on ProjectV2ItemFieldTextValue {
|
|
81
|
+
field { ... on ProjectV2Field { name } }
|
|
82
|
+
text
|
|
83
|
+
}
|
|
84
|
+
... on ProjectV2ItemFieldNumberValue {
|
|
85
|
+
field { ... on ProjectV2Field { name } }
|
|
86
|
+
number
|
|
220
87
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
field { ... on ProjectV2Field { name } }
|
|
225
|
-
date
|
|
226
|
-
}
|
|
227
|
-
... on ProjectV2ItemFieldSingleSelectValue {
|
|
228
|
-
field { ... on ProjectV2SingleSelectField { name } }
|
|
229
|
-
name
|
|
230
|
-
}
|
|
231
|
-
... on ProjectV2ItemFieldTextValue {
|
|
232
|
-
field { ... on ProjectV2Field { name } }
|
|
233
|
-
text
|
|
234
|
-
}
|
|
235
|
-
... on ProjectV2ItemFieldNumberValue {
|
|
236
|
-
field { ... on ProjectV2Field { name } }
|
|
237
|
-
number
|
|
238
|
-
}
|
|
239
|
-
... on ProjectV2ItemFieldIterationValue {
|
|
240
|
-
field { ... on ProjectV2IterationField { name } }
|
|
241
|
-
title
|
|
242
|
-
}
|
|
243
|
-
}
|
|
88
|
+
... on ProjectV2ItemFieldIterationValue {
|
|
89
|
+
field { ... on ProjectV2IterationField { name } }
|
|
90
|
+
title
|
|
244
91
|
}
|
|
245
92
|
}
|
|
246
93
|
}
|
|
@@ -248,6 +95,12 @@ function fetchProjectEnrichment(repo, projectNumber) {
|
|
|
248
95
|
}
|
|
249
96
|
}
|
|
250
97
|
`;
|
|
98
|
+
const query = `
|
|
99
|
+
query($owner: String!, $projectNumber: Int!, $cursor: String) {
|
|
100
|
+
organization(login: $owner) { ${projectItemsFragment} }
|
|
101
|
+
user(login: $owner) { ${projectItemsFragment} }
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
251
104
|
try {
|
|
252
105
|
const enrichMap = /* @__PURE__ */ new Map();
|
|
253
106
|
let cursor = null;
|
|
@@ -263,8 +116,9 @@ function fetchProjectEnrichment(repo, projectNumber) {
|
|
|
263
116
|
`projectNumber=${String(projectNumber)}`
|
|
264
117
|
];
|
|
265
118
|
if (cursor) args.push("-f", `cursor=${cursor}`);
|
|
266
|
-
const result =
|
|
267
|
-
const
|
|
119
|
+
const result = runGhGraphQL(args);
|
|
120
|
+
const ownerNode = result?.data?.organization ?? result?.data?.user;
|
|
121
|
+
const page = ownerNode?.projectV2?.items;
|
|
268
122
|
const nodes = page?.nodes ?? [];
|
|
269
123
|
for (const item of nodes) {
|
|
270
124
|
if (!item?.content?.number) continue;
|
|
@@ -298,24 +152,26 @@ function fetchProjectEnrichment(repo, projectNumber) {
|
|
|
298
152
|
function fetchProjectStatusOptions(repo, projectNumber, _statusFieldId) {
|
|
299
153
|
const [owner] = repo.split("/");
|
|
300
154
|
if (!owner) return [];
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
id
|
|
309
|
-
name
|
|
310
|
-
}
|
|
311
|
-
}
|
|
155
|
+
const statusFragment = `
|
|
156
|
+
projectV2(number: $projectNumber) {
|
|
157
|
+
field(name: "Status") {
|
|
158
|
+
... on ProjectV2SingleSelectField {
|
|
159
|
+
options {
|
|
160
|
+
id
|
|
161
|
+
name
|
|
312
162
|
}
|
|
313
163
|
}
|
|
314
164
|
}
|
|
315
165
|
}
|
|
316
166
|
`;
|
|
167
|
+
const query = `
|
|
168
|
+
query($owner: String!, $projectNumber: Int!) {
|
|
169
|
+
organization(login: $owner) { ${statusFragment} }
|
|
170
|
+
user(login: $owner) { ${statusFragment} }
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
317
173
|
try {
|
|
318
|
-
const result =
|
|
174
|
+
const result = runGhGraphQL([
|
|
319
175
|
"api",
|
|
320
176
|
"graphql",
|
|
321
177
|
"-f",
|
|
@@ -325,7 +181,8 @@ function fetchProjectStatusOptions(repo, projectNumber, _statusFieldId) {
|
|
|
325
181
|
"-F",
|
|
326
182
|
`projectNumber=${String(projectNumber)}`
|
|
327
183
|
]);
|
|
328
|
-
|
|
184
|
+
const ownerNode = result?.data?.organization ?? result?.data?.user;
|
|
185
|
+
return ownerNode?.projectV2?.field?.options ?? [];
|
|
329
186
|
} catch {
|
|
330
187
|
return [];
|
|
331
188
|
}
|
|
@@ -339,13 +196,6 @@ var init_github = __esm({
|
|
|
339
196
|
}
|
|
340
197
|
});
|
|
341
198
|
|
|
342
|
-
// src/types.ts
|
|
343
|
-
var init_types = __esm({
|
|
344
|
-
"src/types.ts"() {
|
|
345
|
-
"use strict";
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
|
|
349
199
|
// src/utils.ts
|
|
350
200
|
function formatError(err) {
|
|
351
201
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -360,6 +210,8 @@ var init_utils = __esm({
|
|
|
360
210
|
var fetch_exports = {};
|
|
361
211
|
__export(fetch_exports, {
|
|
362
212
|
SLACK_URL_RE: () => SLACK_URL_RE,
|
|
213
|
+
extractIssueNumbersFromBranch: () => extractIssueNumbersFromBranch,
|
|
214
|
+
extractLinkedIssueNumbers: () => extractLinkedIssueNumbers,
|
|
363
215
|
extractSlackUrl: () => extractSlackUrl,
|
|
364
216
|
fetchDashboard: () => fetchDashboard,
|
|
365
217
|
fetchRecentActivity: () => fetchRecentActivity
|
|
@@ -370,6 +222,17 @@ function extractSlackUrl(body) {
|
|
|
370
222
|
const match = body.match(SLACK_URL_RE);
|
|
371
223
|
return match?.[0];
|
|
372
224
|
}
|
|
225
|
+
function extractIssueNumbersFromBranch(branchName) {
|
|
226
|
+
const matches = branchName.match(/\b(\d{1,5})\b/g);
|
|
227
|
+
if (!matches) return [];
|
|
228
|
+
return [...new Set(matches.map((m) => parseInt(m, 10)).filter((n) => n > 0))];
|
|
229
|
+
}
|
|
230
|
+
function extractLinkedIssueNumbers(title, body) {
|
|
231
|
+
const text = `${title ?? ""} ${body ?? ""}`;
|
|
232
|
+
const matches = text.match(/#(\d{1,5})\b/g);
|
|
233
|
+
if (!matches) return [];
|
|
234
|
+
return [...new Set(matches.map((m) => parseInt(m.slice(1), 10)).filter((n) => n > 0))];
|
|
235
|
+
}
|
|
373
236
|
function fetchRecentActivity(repoName, shortName) {
|
|
374
237
|
try {
|
|
375
238
|
const output = execFileSync2(
|
|
@@ -377,9 +240,10 @@ function fetchRecentActivity(repoName, shortName) {
|
|
|
377
240
|
[
|
|
378
241
|
"api",
|
|
379
242
|
`repos/${repoName}/events`,
|
|
380
|
-
"
|
|
243
|
+
"-f",
|
|
244
|
+
"per_page=30",
|
|
381
245
|
"-q",
|
|
382
|
-
'.[] | select(.type == "IssuesEvent" or .type == "IssueCommentEvent" or .type == "PullRequestEvent") | {type: .type, actor: .actor.login, action: .payload.action, number: (.payload.issue.number // .payload.pull_request.number), title: (.payload.issue.title // .payload.pull_request.title), body: .payload.comment.body, created_at: .created_at}'
|
|
246
|
+
'.[] | select(.type == "IssuesEvent" or .type == "IssueCommentEvent" or .type == "PullRequestEvent" or .type == "CreateEvent") | {type: .type, actor: .actor.login, action: .payload.action, number: (.payload.issue.number // .payload.pull_request.number), title: (.payload.issue.title // .payload.pull_request.title), body: (.payload.comment.body // .payload.pull_request.body), created_at: .created_at, ref: .payload.ref, ref_type: .payload.ref_type, merged: .payload.pull_request.merged}'
|
|
383
247
|
],
|
|
384
248
|
{ encoding: "utf-8", timeout: 15e3 }
|
|
385
249
|
);
|
|
@@ -391,9 +255,26 @@ function fetchRecentActivity(repoName, shortName) {
|
|
|
391
255
|
const ev = JSON.parse(line);
|
|
392
256
|
const timestamp = new Date(ev.created_at);
|
|
393
257
|
if (timestamp.getTime() < cutoff) continue;
|
|
258
|
+
if (ev.type === "CreateEvent") {
|
|
259
|
+
if (ev.ref_type !== "branch" || !ev.ref) continue;
|
|
260
|
+
const issueNumbers = extractIssueNumbersFromBranch(ev.ref);
|
|
261
|
+
for (const num of issueNumbers) {
|
|
262
|
+
events.push({
|
|
263
|
+
type: "branch_created",
|
|
264
|
+
repoShortName: shortName,
|
|
265
|
+
issueNumber: num,
|
|
266
|
+
actor: ev.actor,
|
|
267
|
+
summary: `created branch ${ev.ref}`,
|
|
268
|
+
timestamp,
|
|
269
|
+
branchName: ev.ref
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
394
274
|
if (!ev.number) continue;
|
|
395
275
|
let eventType;
|
|
396
276
|
let summary;
|
|
277
|
+
let extras = {};
|
|
397
278
|
if (ev.type === "IssueCommentEvent") {
|
|
398
279
|
eventType = "comment";
|
|
399
280
|
const preview = ev.body ? ev.body.slice(0, 60).replace(/\n/g, " ") : "";
|
|
@@ -419,6 +300,45 @@ function fetchRecentActivity(repoName, shortName) {
|
|
|
419
300
|
default:
|
|
420
301
|
continue;
|
|
421
302
|
}
|
|
303
|
+
} else if (ev.type === "PullRequestEvent") {
|
|
304
|
+
const prNumber = ev.number;
|
|
305
|
+
extras = { prNumber };
|
|
306
|
+
if (ev.action === "opened") {
|
|
307
|
+
eventType = "pr_opened";
|
|
308
|
+
summary = `opened PR #${prNumber}: ${ev.title ?? ""}`;
|
|
309
|
+
} else if (ev.action === "closed" && ev.merged) {
|
|
310
|
+
eventType = "pr_merged";
|
|
311
|
+
summary = `merged PR #${prNumber}: ${ev.title ?? ""}`;
|
|
312
|
+
} else if (ev.action === "closed") {
|
|
313
|
+
eventType = "pr_closed";
|
|
314
|
+
summary = `closed PR #${prNumber}`;
|
|
315
|
+
} else {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const linkedIssues = extractLinkedIssueNumbers(ev.title, ev.body);
|
|
319
|
+
for (const issueNum of linkedIssues) {
|
|
320
|
+
events.push({
|
|
321
|
+
type: eventType,
|
|
322
|
+
repoShortName: shortName,
|
|
323
|
+
issueNumber: issueNum,
|
|
324
|
+
actor: ev.actor,
|
|
325
|
+
summary,
|
|
326
|
+
timestamp,
|
|
327
|
+
prNumber
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
if (linkedIssues.length === 0) {
|
|
331
|
+
events.push({
|
|
332
|
+
type: eventType,
|
|
333
|
+
repoShortName: shortName,
|
|
334
|
+
issueNumber: prNumber,
|
|
335
|
+
actor: ev.actor,
|
|
336
|
+
summary,
|
|
337
|
+
timestamp,
|
|
338
|
+
prNumber
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
continue;
|
|
422
342
|
} else {
|
|
423
343
|
continue;
|
|
424
344
|
}
|
|
@@ -428,7 +348,8 @@ function fetchRecentActivity(repoName, shortName) {
|
|
|
428
348
|
issueNumber: ev.number,
|
|
429
349
|
actor: ev.actor,
|
|
430
350
|
summary,
|
|
431
|
-
timestamp
|
|
351
|
+
timestamp,
|
|
352
|
+
...extras
|
|
432
353
|
});
|
|
433
354
|
} catch {
|
|
434
355
|
}
|
|
@@ -480,20 +401,6 @@ async function fetchDashboard(config2, options2 = {}) {
|
|
|
480
401
|
return { repo, issues: [], statusOptions: [], error: formatError(err) };
|
|
481
402
|
}
|
|
482
403
|
});
|
|
483
|
-
let ticktick = [];
|
|
484
|
-
let ticktickError = null;
|
|
485
|
-
if (config2.ticktick.enabled) {
|
|
486
|
-
try {
|
|
487
|
-
const auth = requireAuth();
|
|
488
|
-
const api = new TickTickClient(auth.accessToken);
|
|
489
|
-
if (config2.defaultProjectId) {
|
|
490
|
-
const tasks = await api.listTasks(config2.defaultProjectId);
|
|
491
|
-
ticktick = tasks.filter((t) => t.status !== 2 /* Completed */);
|
|
492
|
-
}
|
|
493
|
-
} catch (err) {
|
|
494
|
-
ticktickError = formatError(err);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
404
|
const activity = [];
|
|
498
405
|
for (const repo of repos) {
|
|
499
406
|
const events = fetchRecentActivity(repo.name, repo.shortName);
|
|
@@ -502,8 +409,6 @@ async function fetchDashboard(config2, options2 = {}) {
|
|
|
502
409
|
activity.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
503
410
|
return {
|
|
504
411
|
repos: repoData,
|
|
505
|
-
ticktick,
|
|
506
|
-
ticktickError,
|
|
507
412
|
activity: activity.slice(0, 15),
|
|
508
413
|
fetchedAt: /* @__PURE__ */ new Date()
|
|
509
414
|
};
|
|
@@ -512,10 +417,7 @@ var SLACK_URL_RE;
|
|
|
512
417
|
var init_fetch = __esm({
|
|
513
418
|
"src/board/fetch.ts"() {
|
|
514
419
|
"use strict";
|
|
515
|
-
init_api();
|
|
516
|
-
init_config();
|
|
517
420
|
init_github();
|
|
518
|
-
init_types();
|
|
519
421
|
init_utils();
|
|
520
422
|
SLACK_URL_RE = /https:\/\/[^/]+\.slack\.com\/archives\/[A-Z0-9]+\/p[0-9]+/i;
|
|
521
423
|
}
|