@bretwardjames/ghp-core 0.6.1 → 0.7.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/index.cjs +550 -2
- package/dist/index.d.cts +93 -3
- package/dist/index.d.ts +93 -3
- package/dist/index.js +548 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -78,6 +78,7 @@ __export(index_exports, {
|
|
|
78
78
|
findSessionFile: () => findSessionFile,
|
|
79
79
|
formatAction: () => formatAction,
|
|
80
80
|
formatConflict: () => formatConflict,
|
|
81
|
+
formatStandupText: () => formatStandupText,
|
|
81
82
|
gatherDashboardData: () => gatherDashboardData,
|
|
82
83
|
generateBranchName: () => generateBranchName,
|
|
83
84
|
generateWorktreePath: () => generateWorktreePath,
|
|
@@ -135,6 +136,7 @@ __export(index_exports, {
|
|
|
135
136
|
parseIssueUrl: () => parseIssueUrl,
|
|
136
137
|
parseRateLimitDelay: () => parseRateLimitDelay,
|
|
137
138
|
parseSessionLine: () => parseSessionLine,
|
|
139
|
+
parseSince: () => parseSince,
|
|
138
140
|
pullLatest: () => pullLatest,
|
|
139
141
|
queries: () => queries_exports,
|
|
140
142
|
registerAgent: () => registerAgent,
|
|
@@ -190,12 +192,15 @@ __export(queries_exports, {
|
|
|
190
192
|
ISSUE_FOR_UPDATE_QUERY: () => ISSUE_FOR_UPDATE_QUERY,
|
|
191
193
|
ISSUE_NODE_ID_QUERY: () => ISSUE_NODE_ID_QUERY,
|
|
192
194
|
ISSUE_RELATIONSHIPS_QUERY: () => ISSUE_RELATIONSHIPS_QUERY,
|
|
195
|
+
ISSUE_TIMELINE_QUERY: () => ISSUE_TIMELINE_QUERY,
|
|
193
196
|
ISSUE_TYPES_QUERY: () => ISSUE_TYPES_QUERY,
|
|
194
197
|
ISSUE_WITH_PROJECT_ITEMS_QUERY: () => ISSUE_WITH_PROJECT_ITEMS_QUERY,
|
|
195
198
|
LABEL_EXISTS_QUERY: () => LABEL_EXISTS_QUERY,
|
|
196
199
|
PROJECT_FIELDS_QUERY: () => PROJECT_FIELDS_QUERY,
|
|
197
200
|
PROJECT_ITEMS_QUERY: () => PROJECT_ITEMS_QUERY,
|
|
198
201
|
PROJECT_VIEWS_QUERY: () => PROJECT_VIEWS_QUERY,
|
|
202
|
+
PR_AUTHORED_SEARCH_QUERY: () => PR_AUTHORED_SEARCH_QUERY,
|
|
203
|
+
PR_REVIEWS_SEARCH_QUERY: () => PR_REVIEWS_SEARCH_QUERY,
|
|
199
204
|
RECENT_ISSUES_QUERY: () => RECENT_ISSUES_QUERY,
|
|
200
205
|
REMOVE_BLOCKED_BY_MUTATION: () => REMOVE_BLOCKED_BY_MUTATION,
|
|
201
206
|
REMOVE_LABELS_MUTATION: () => REMOVE_LABELS_MUTATION,
|
|
@@ -280,6 +285,7 @@ var PROJECT_ITEMS_QUERY = `
|
|
|
280
285
|
number
|
|
281
286
|
url
|
|
282
287
|
state
|
|
288
|
+
updatedAt
|
|
283
289
|
issueType { name }
|
|
284
290
|
assignees(first: 5) { nodes { login } }
|
|
285
291
|
labels(first: 10) { nodes { name color } }
|
|
@@ -294,6 +300,7 @@ var PROJECT_ITEMS_QUERY = `
|
|
|
294
300
|
number
|
|
295
301
|
url
|
|
296
302
|
state
|
|
303
|
+
updatedAt
|
|
297
304
|
merged
|
|
298
305
|
assignees(first: 5) { nodes { login } }
|
|
299
306
|
labels(first: 10) { nodes { name color } }
|
|
@@ -657,6 +664,162 @@ var ISSUE_WITH_PROJECT_ITEMS_QUERY = `
|
|
|
657
664
|
}
|
|
658
665
|
}
|
|
659
666
|
`;
|
|
667
|
+
var ISSUE_TIMELINE_QUERY = `
|
|
668
|
+
query($owner: String!, $name: String!, $number: Int!, $since: DateTime!) {
|
|
669
|
+
repository(owner: $owner, name: $name) {
|
|
670
|
+
issueOrPullRequest(number: $number) {
|
|
671
|
+
... on Issue {
|
|
672
|
+
timelineItems(first: 100, since: $since) {
|
|
673
|
+
nodes {
|
|
674
|
+
__typename
|
|
675
|
+
... on IssueComment {
|
|
676
|
+
author { login }
|
|
677
|
+
createdAt
|
|
678
|
+
body
|
|
679
|
+
}
|
|
680
|
+
... on LabeledEvent {
|
|
681
|
+
actor { login }
|
|
682
|
+
createdAt
|
|
683
|
+
label { name }
|
|
684
|
+
}
|
|
685
|
+
... on UnlabeledEvent {
|
|
686
|
+
actor { login }
|
|
687
|
+
createdAt
|
|
688
|
+
label { name }
|
|
689
|
+
}
|
|
690
|
+
... on AssignedEvent {
|
|
691
|
+
actor { login }
|
|
692
|
+
createdAt
|
|
693
|
+
assignee { ... on User { login } }
|
|
694
|
+
}
|
|
695
|
+
... on UnassignedEvent {
|
|
696
|
+
actor { login }
|
|
697
|
+
createdAt
|
|
698
|
+
assignee { ... on User { login } }
|
|
699
|
+
}
|
|
700
|
+
... on ClosedEvent {
|
|
701
|
+
actor { login }
|
|
702
|
+
createdAt
|
|
703
|
+
}
|
|
704
|
+
... on ReopenedEvent {
|
|
705
|
+
actor { login }
|
|
706
|
+
createdAt
|
|
707
|
+
}
|
|
708
|
+
... on CrossReferencedEvent {
|
|
709
|
+
actor { login }
|
|
710
|
+
createdAt
|
|
711
|
+
source {
|
|
712
|
+
__typename
|
|
713
|
+
... on PullRequest { number title url }
|
|
714
|
+
... on Issue { number title }
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
... on PullRequest {
|
|
721
|
+
timelineItems(first: 100, since: $since) {
|
|
722
|
+
nodes {
|
|
723
|
+
__typename
|
|
724
|
+
... on IssueComment {
|
|
725
|
+
author { login }
|
|
726
|
+
createdAt
|
|
727
|
+
body
|
|
728
|
+
}
|
|
729
|
+
... on LabeledEvent {
|
|
730
|
+
actor { login }
|
|
731
|
+
createdAt
|
|
732
|
+
label { name }
|
|
733
|
+
}
|
|
734
|
+
... on UnlabeledEvent {
|
|
735
|
+
actor { login }
|
|
736
|
+
createdAt
|
|
737
|
+
label { name }
|
|
738
|
+
}
|
|
739
|
+
... on AssignedEvent {
|
|
740
|
+
actor { login }
|
|
741
|
+
createdAt
|
|
742
|
+
assignee { ... on User { login } }
|
|
743
|
+
}
|
|
744
|
+
... on UnassignedEvent {
|
|
745
|
+
actor { login }
|
|
746
|
+
createdAt
|
|
747
|
+
assignee { ... on User { login } }
|
|
748
|
+
}
|
|
749
|
+
... on ClosedEvent {
|
|
750
|
+
actor { login }
|
|
751
|
+
createdAt
|
|
752
|
+
}
|
|
753
|
+
... on ReopenedEvent {
|
|
754
|
+
actor { login }
|
|
755
|
+
createdAt
|
|
756
|
+
}
|
|
757
|
+
... on CrossReferencedEvent {
|
|
758
|
+
actor { login }
|
|
759
|
+
createdAt
|
|
760
|
+
source {
|
|
761
|
+
__typename
|
|
762
|
+
... on PullRequest { number title url }
|
|
763
|
+
... on Issue { number title }
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
... on PullRequestReview {
|
|
767
|
+
author { login }
|
|
768
|
+
createdAt
|
|
769
|
+
state
|
|
770
|
+
}
|
|
771
|
+
... on ReviewRequestedEvent {
|
|
772
|
+
actor { login }
|
|
773
|
+
createdAt
|
|
774
|
+
requestedReviewer {
|
|
775
|
+
... on User { login }
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
`;
|
|
785
|
+
var PR_REVIEWS_SEARCH_QUERY = `
|
|
786
|
+
query($searchQuery: String!) {
|
|
787
|
+
search(query: $searchQuery, type: ISSUE, first: 50) {
|
|
788
|
+
nodes {
|
|
789
|
+
... on PullRequest {
|
|
790
|
+
number
|
|
791
|
+
title
|
|
792
|
+
url
|
|
793
|
+
author { login }
|
|
794
|
+
reviews(first: 20) {
|
|
795
|
+
nodes {
|
|
796
|
+
author { login }
|
|
797
|
+
state
|
|
798
|
+
submittedAt
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
`;
|
|
806
|
+
var PR_AUTHORED_SEARCH_QUERY = `
|
|
807
|
+
query($searchQuery: String!) {
|
|
808
|
+
search(query: $searchQuery, type: ISSUE, first: 50) {
|
|
809
|
+
nodes {
|
|
810
|
+
... on PullRequest {
|
|
811
|
+
number
|
|
812
|
+
title
|
|
813
|
+
url
|
|
814
|
+
state
|
|
815
|
+
createdAt
|
|
816
|
+
mergedAt
|
|
817
|
+
mergedBy { login }
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
`;
|
|
660
823
|
var ISSUE_RELATIONSHIPS_QUERY = `
|
|
661
824
|
query($owner: String!, $name: String!, $number: Int!) {
|
|
662
825
|
repository(owner: $owner, name: $name) {
|
|
@@ -1062,7 +1225,8 @@ var GitHubAPI = class {
|
|
|
1062
1225
|
parent,
|
|
1063
1226
|
subIssues,
|
|
1064
1227
|
blockedBy,
|
|
1065
|
-
blocking
|
|
1228
|
+
blocking,
|
|
1229
|
+
updatedAt: content.updatedAt || null
|
|
1066
1230
|
};
|
|
1067
1231
|
});
|
|
1068
1232
|
}
|
|
@@ -1171,7 +1335,8 @@ var GitHubAPI = class {
|
|
|
1171
1335
|
parent: issue.parent,
|
|
1172
1336
|
subIssues: issue.subIssues.nodes,
|
|
1173
1337
|
blockedBy: issue.blockedBy.nodes,
|
|
1174
|
-
blocking: issue.blocking.nodes
|
|
1338
|
+
blocking: issue.blocking.nodes,
|
|
1339
|
+
updatedAt: null
|
|
1175
1340
|
};
|
|
1176
1341
|
} catch (error) {
|
|
1177
1342
|
return null;
|
|
@@ -1733,6 +1898,279 @@ var GitHubAPI = class {
|
|
|
1733
1898
|
return null;
|
|
1734
1899
|
}
|
|
1735
1900
|
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Get recent activity across all project items since a given time.
|
|
1903
|
+
* Multi-pass approach:
|
|
1904
|
+
* Pass 1: Fetch all project items, filter by updatedAt client-side
|
|
1905
|
+
* Pass 2: Fetch timeline events only for items that changed
|
|
1906
|
+
* Pass 3: Search for PRs reviewed by user (not captured in project items)
|
|
1907
|
+
* Pass 4: Search for PRs authored by user (created/merged)
|
|
1908
|
+
*/
|
|
1909
|
+
async getRecentActivity(repo, since, options) {
|
|
1910
|
+
if (!this.graphqlWithAuth) throw new Error("Not authenticated");
|
|
1911
|
+
const projects = await this.getProjects(repo);
|
|
1912
|
+
if (projects.length === 0) return [];
|
|
1913
|
+
const allItems = [];
|
|
1914
|
+
for (const project of projects) {
|
|
1915
|
+
const items = await this.getProjectItems(project.id, project.title);
|
|
1916
|
+
allItems.push(...items);
|
|
1917
|
+
}
|
|
1918
|
+
const sinceMs = since.getTime();
|
|
1919
|
+
const sinceISO = since.toISOString();
|
|
1920
|
+
const sinceDate = sinceISO.split("T")[0];
|
|
1921
|
+
let recentItems = allItems.filter(
|
|
1922
|
+
(item) => item.updatedAt && new Date(item.updatedAt).getTime() >= sinceMs
|
|
1923
|
+
);
|
|
1924
|
+
if (options?.mine && this.username) {
|
|
1925
|
+
recentItems = recentItems.filter(
|
|
1926
|
+
(item) => item.assignees.includes(this.username)
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1929
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1930
|
+
recentItems = recentItems.filter((item) => {
|
|
1931
|
+
const key = `${item.repository || ""}#${item.number}`;
|
|
1932
|
+
if (!item.number || seen.has(key)) return false;
|
|
1933
|
+
seen.add(key);
|
|
1934
|
+
return true;
|
|
1935
|
+
});
|
|
1936
|
+
const activities = [];
|
|
1937
|
+
const BATCH_SIZE = 5;
|
|
1938
|
+
for (let i = 0; i < recentItems.length; i += BATCH_SIZE) {
|
|
1939
|
+
const batch = recentItems.slice(i, i + BATCH_SIZE);
|
|
1940
|
+
const results = await Promise.allSettled(
|
|
1941
|
+
batch.map(async (item) => {
|
|
1942
|
+
if (!item.number || !item.repository) return null;
|
|
1943
|
+
const [owner, name] = item.repository.split("/");
|
|
1944
|
+
if (!owner || !name) return null;
|
|
1945
|
+
const events = await this.fetchTimelineEvents(
|
|
1946
|
+
owner,
|
|
1947
|
+
name,
|
|
1948
|
+
item.number,
|
|
1949
|
+
sinceISO
|
|
1950
|
+
);
|
|
1951
|
+
if (events.length === 0) return null;
|
|
1952
|
+
return {
|
|
1953
|
+
issue: {
|
|
1954
|
+
number: item.number,
|
|
1955
|
+
title: item.title,
|
|
1956
|
+
url: item.url || ""
|
|
1957
|
+
},
|
|
1958
|
+
status: item.status,
|
|
1959
|
+
assignees: item.assignees,
|
|
1960
|
+
changes: events
|
|
1961
|
+
};
|
|
1962
|
+
})
|
|
1963
|
+
);
|
|
1964
|
+
for (const result of results) {
|
|
1965
|
+
if (result.status === "fulfilled" && result.value) {
|
|
1966
|
+
activities.push(result.value);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
const activityNumbers = new Set(activities.map((a) => a.issue.number));
|
|
1971
|
+
if (this.username) {
|
|
1972
|
+
const reviewActivities = await this.fetchReviewedPRs(
|
|
1973
|
+
repo,
|
|
1974
|
+
this.username,
|
|
1975
|
+
sinceDate,
|
|
1976
|
+
sinceMs,
|
|
1977
|
+
activityNumbers
|
|
1978
|
+
);
|
|
1979
|
+
for (const a of reviewActivities) {
|
|
1980
|
+
activityNumbers.add(a.issue.number);
|
|
1981
|
+
activities.push(a);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
if (this.username) {
|
|
1985
|
+
const authoredActivities = await this.fetchAuthoredPRs(
|
|
1986
|
+
repo,
|
|
1987
|
+
this.username,
|
|
1988
|
+
sinceDate,
|
|
1989
|
+
sinceMs,
|
|
1990
|
+
activityNumbers
|
|
1991
|
+
);
|
|
1992
|
+
for (const a of authoredActivities) {
|
|
1993
|
+
activityNumbers.add(a.issue.number);
|
|
1994
|
+
activities.push(a);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
activities.sort((a, b) => {
|
|
1998
|
+
const aLatest = a.changes[a.changes.length - 1]?.timestamp || "";
|
|
1999
|
+
const bLatest = b.changes[b.changes.length - 1]?.timestamp || "";
|
|
2000
|
+
return bLatest.localeCompare(aLatest);
|
|
2001
|
+
});
|
|
2002
|
+
return activities;
|
|
2003
|
+
}
|
|
2004
|
+
/**
|
|
2005
|
+
* Search for PRs the user reviewed that aren't already in the activity list.
|
|
2006
|
+
*/
|
|
2007
|
+
async fetchReviewedPRs(repo, username, sinceDate, sinceMs, seen) {
|
|
2008
|
+
const searchQuery = `repo:${repo.owner}/${repo.name} reviewed-by:${username} updated:>=${sinceDate} is:pr`;
|
|
2009
|
+
const response = await this.graphqlWithRetry(
|
|
2010
|
+
PR_REVIEWS_SEARCH_QUERY,
|
|
2011
|
+
{ searchQuery }
|
|
2012
|
+
);
|
|
2013
|
+
const activities = [];
|
|
2014
|
+
for (const pr of response.search.nodes) {
|
|
2015
|
+
if (seen.has(pr.number)) continue;
|
|
2016
|
+
const recentReviews = pr.reviews.nodes.filter(
|
|
2017
|
+
(r) => r.author?.login === username && new Date(r.submittedAt).getTime() >= sinceMs
|
|
2018
|
+
);
|
|
2019
|
+
if (recentReviews.length === 0) continue;
|
|
2020
|
+
const events = recentReviews.map((r) => {
|
|
2021
|
+
const stateLabel = r.state === "APPROVED" ? "Approved" : r.state === "CHANGES_REQUESTED" ? "Changes requested" : r.state === "COMMENTED" ? "Review comment" : r.state === "DISMISSED" ? "Review dismissed" : r.state;
|
|
2022
|
+
return {
|
|
2023
|
+
type: "review_submitted",
|
|
2024
|
+
actor: username,
|
|
2025
|
+
timestamp: r.submittedAt,
|
|
2026
|
+
details: stateLabel
|
|
2027
|
+
};
|
|
2028
|
+
});
|
|
2029
|
+
activities.push({
|
|
2030
|
+
issue: { number: pr.number, title: pr.title, url: pr.url },
|
|
2031
|
+
status: null,
|
|
2032
|
+
assignees: [],
|
|
2033
|
+
changes: events
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
return activities;
|
|
2037
|
+
}
|
|
2038
|
+
/**
|
|
2039
|
+
* Search for PRs the user authored that aren't already in the activity list.
|
|
2040
|
+
* Captures PR creation and merges.
|
|
2041
|
+
*/
|
|
2042
|
+
async fetchAuthoredPRs(repo, username, sinceDate, sinceMs, seen) {
|
|
2043
|
+
const searchQuery = `repo:${repo.owner}/${repo.name} author:${username} created:>=${sinceDate} is:pr`;
|
|
2044
|
+
const response = await this.graphqlWithRetry(
|
|
2045
|
+
PR_AUTHORED_SEARCH_QUERY,
|
|
2046
|
+
{ searchQuery }
|
|
2047
|
+
);
|
|
2048
|
+
const activities = [];
|
|
2049
|
+
for (const pr of response.search.nodes) {
|
|
2050
|
+
if (seen.has(pr.number)) continue;
|
|
2051
|
+
const events = [];
|
|
2052
|
+
if (new Date(pr.createdAt).getTime() >= sinceMs) {
|
|
2053
|
+
events.push({
|
|
2054
|
+
type: "pr_created",
|
|
2055
|
+
actor: username,
|
|
2056
|
+
timestamp: pr.createdAt
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
if (pr.mergedAt && new Date(pr.mergedAt).getTime() >= sinceMs) {
|
|
2060
|
+
events.push({
|
|
2061
|
+
type: "pr_merged",
|
|
2062
|
+
actor: pr.mergedBy?.login || username,
|
|
2063
|
+
timestamp: pr.mergedAt
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
if (events.length === 0) continue;
|
|
2067
|
+
activities.push({
|
|
2068
|
+
issue: { number: pr.number, title: pr.title, url: pr.url },
|
|
2069
|
+
status: pr.state === "MERGED" ? "Merged" : pr.state === "OPEN" ? "Open" : "Closed",
|
|
2070
|
+
assignees: [username],
|
|
2071
|
+
changes: events
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
return activities;
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Fetch and normalize timeline events for a single issue/PR
|
|
2078
|
+
*/
|
|
2079
|
+
async fetchTimelineEvents(owner, name, number, since) {
|
|
2080
|
+
if (!this.graphqlWithAuth) throw new Error("Not authenticated");
|
|
2081
|
+
const response = await this.graphqlWithRetry(
|
|
2082
|
+
ISSUE_TIMELINE_QUERY,
|
|
2083
|
+
{ owner, name, number, since }
|
|
2084
|
+
);
|
|
2085
|
+
const item = response.repository.issueOrPullRequest;
|
|
2086
|
+
if (!item) return [];
|
|
2087
|
+
const events = [];
|
|
2088
|
+
for (const node of item.timelineItems.nodes) {
|
|
2089
|
+
const actor = node.actor?.login || node.author?.login || "unknown";
|
|
2090
|
+
const timestamp = node.createdAt || "";
|
|
2091
|
+
switch (node.__typename) {
|
|
2092
|
+
case "IssueComment":
|
|
2093
|
+
events.push({
|
|
2094
|
+
type: "comment",
|
|
2095
|
+
actor,
|
|
2096
|
+
timestamp,
|
|
2097
|
+
details: node.body ? node.body.substring(0, 80) + (node.body.length > 80 ? "..." : "") : void 0
|
|
2098
|
+
});
|
|
2099
|
+
break;
|
|
2100
|
+
case "LabeledEvent":
|
|
2101
|
+
events.push({
|
|
2102
|
+
type: "labeled",
|
|
2103
|
+
actor,
|
|
2104
|
+
timestamp,
|
|
2105
|
+
details: node.label?.name
|
|
2106
|
+
});
|
|
2107
|
+
break;
|
|
2108
|
+
case "UnlabeledEvent":
|
|
2109
|
+
events.push({
|
|
2110
|
+
type: "unlabeled",
|
|
2111
|
+
actor,
|
|
2112
|
+
timestamp,
|
|
2113
|
+
details: node.label?.name
|
|
2114
|
+
});
|
|
2115
|
+
break;
|
|
2116
|
+
case "AssignedEvent":
|
|
2117
|
+
events.push({
|
|
2118
|
+
type: "assigned",
|
|
2119
|
+
actor,
|
|
2120
|
+
timestamp,
|
|
2121
|
+
details: node.assignee?.login
|
|
2122
|
+
});
|
|
2123
|
+
break;
|
|
2124
|
+
case "UnassignedEvent":
|
|
2125
|
+
events.push({
|
|
2126
|
+
type: "unassigned",
|
|
2127
|
+
actor,
|
|
2128
|
+
timestamp,
|
|
2129
|
+
details: node.assignee?.login
|
|
2130
|
+
});
|
|
2131
|
+
break;
|
|
2132
|
+
case "ClosedEvent":
|
|
2133
|
+
events.push({ type: "closed", actor, timestamp });
|
|
2134
|
+
break;
|
|
2135
|
+
case "ReopenedEvent":
|
|
2136
|
+
events.push({ type: "reopened", actor, timestamp });
|
|
2137
|
+
break;
|
|
2138
|
+
case "CrossReferencedEvent": {
|
|
2139
|
+
const source = node.source;
|
|
2140
|
+
if (source) {
|
|
2141
|
+
const ref = source.__typename === "PullRequest" ? `PR #${source.number}: ${source.title}` : `#${source.number}: ${source.title}`;
|
|
2142
|
+
events.push({
|
|
2143
|
+
type: "referenced",
|
|
2144
|
+
actor,
|
|
2145
|
+
timestamp,
|
|
2146
|
+
details: ref
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
break;
|
|
2150
|
+
}
|
|
2151
|
+
case "PullRequestReview": {
|
|
2152
|
+
const stateLabel = node.state === "APPROVED" ? "Approved" : node.state === "CHANGES_REQUESTED" ? "Changes requested" : node.state === "COMMENTED" ? "Review comment" : node.state === "DISMISSED" ? "Review dismissed" : node.state || "Reviewed";
|
|
2153
|
+
events.push({
|
|
2154
|
+
type: "review_submitted",
|
|
2155
|
+
actor,
|
|
2156
|
+
timestamp,
|
|
2157
|
+
details: stateLabel
|
|
2158
|
+
});
|
|
2159
|
+
break;
|
|
2160
|
+
}
|
|
2161
|
+
case "ReviewRequestedEvent": {
|
|
2162
|
+
events.push({
|
|
2163
|
+
type: "review_requested",
|
|
2164
|
+
actor,
|
|
2165
|
+
timestamp,
|
|
2166
|
+
details: node.requestedReviewer?.login
|
|
2167
|
+
});
|
|
2168
|
+
break;
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
return events;
|
|
2173
|
+
}
|
|
1736
2174
|
};
|
|
1737
2175
|
|
|
1738
2176
|
// src/branch-linker.ts
|
|
@@ -3804,6 +4242,114 @@ function validateUrl(url) {
|
|
|
3804
4242
|
return url;
|
|
3805
4243
|
}
|
|
3806
4244
|
|
|
4245
|
+
// src/standup.ts
|
|
4246
|
+
function formatStandupText(activities, options) {
|
|
4247
|
+
const { since } = options;
|
|
4248
|
+
const lines = [];
|
|
4249
|
+
const sinceStr = formatRelativeDate(since);
|
|
4250
|
+
const issueCount = activities.length;
|
|
4251
|
+
lines.push(`Since ${sinceStr} \u2014 ${issueCount} issue${issueCount !== 1 ? "s" : ""} changed`);
|
|
4252
|
+
lines.push("");
|
|
4253
|
+
if (activities.length === 0) {
|
|
4254
|
+
lines.push("No activity found in this time window.");
|
|
4255
|
+
return lines.join("\n");
|
|
4256
|
+
}
|
|
4257
|
+
for (const activity of activities) {
|
|
4258
|
+
const statusTag = activity.status ? ` [${activity.status}]` : "";
|
|
4259
|
+
lines.push(`#${activity.issue.number} ${activity.issue.title}${statusTag}`);
|
|
4260
|
+
for (const event of activity.changes) {
|
|
4261
|
+
lines.push(` ${formatEventLine(event)}`);
|
|
4262
|
+
}
|
|
4263
|
+
lines.push("");
|
|
4264
|
+
}
|
|
4265
|
+
return lines.join("\n").trimEnd();
|
|
4266
|
+
}
|
|
4267
|
+
function parseSince(input) {
|
|
4268
|
+
const isoDate = new Date(input);
|
|
4269
|
+
if (!isNaN(isoDate.getTime()) && input.includes("-")) {
|
|
4270
|
+
return isoDate;
|
|
4271
|
+
}
|
|
4272
|
+
const match = input.match(/^(\d+)\s*(h|d|w)$/i);
|
|
4273
|
+
if (!match) {
|
|
4274
|
+
throw new Error(`Invalid duration format: "${input}". Use formats like "24h", "2d", "1w", or an ISO date.`);
|
|
4275
|
+
}
|
|
4276
|
+
const amount = parseInt(match[1], 10);
|
|
4277
|
+
const unit = match[2].toLowerCase();
|
|
4278
|
+
const now = /* @__PURE__ */ new Date();
|
|
4279
|
+
switch (unit) {
|
|
4280
|
+
case "h":
|
|
4281
|
+
return new Date(now.getTime() - amount * 60 * 60 * 1e3);
|
|
4282
|
+
case "d":
|
|
4283
|
+
return new Date(now.getTime() - amount * 24 * 60 * 60 * 1e3);
|
|
4284
|
+
case "w":
|
|
4285
|
+
return new Date(now.getTime() - amount * 7 * 24 * 60 * 60 * 1e3);
|
|
4286
|
+
default:
|
|
4287
|
+
throw new Error(`Unknown duration unit: "${unit}"`);
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
function formatEventLine(event) {
|
|
4291
|
+
const arrow = "\u2197";
|
|
4292
|
+
const timestamp = formatShortTimestamp(event.timestamp);
|
|
4293
|
+
const actor = event.actor;
|
|
4294
|
+
switch (event.type) {
|
|
4295
|
+
case "comment":
|
|
4296
|
+
return `${arrow} Comment by ${actor} (${timestamp})${event.details ? ": " + event.details : ""}`;
|
|
4297
|
+
case "labeled":
|
|
4298
|
+
return `${arrow} Labeled "${event.details}" by ${actor} (${timestamp})`;
|
|
4299
|
+
case "unlabeled":
|
|
4300
|
+
return `${arrow} Unlabeled "${event.details}" by ${actor} (${timestamp})`;
|
|
4301
|
+
case "assigned":
|
|
4302
|
+
return `${arrow} Assigned to ${event.details || actor} (${timestamp})`;
|
|
4303
|
+
case "unassigned":
|
|
4304
|
+
return `${arrow} Unassigned ${event.details || ""} by ${actor} (${timestamp})`;
|
|
4305
|
+
case "closed":
|
|
4306
|
+
return `${arrow} Closed by ${actor} (${timestamp})`;
|
|
4307
|
+
case "reopened":
|
|
4308
|
+
return `${arrow} Reopened by ${actor} (${timestamp})`;
|
|
4309
|
+
case "referenced":
|
|
4310
|
+
return `${arrow} ${event.details} linked by ${actor} (${timestamp})`;
|
|
4311
|
+
case "review_submitted":
|
|
4312
|
+
return `${arrow} ${event.details} by ${actor} (${timestamp})`;
|
|
4313
|
+
case "review_requested":
|
|
4314
|
+
return `${arrow} Review requested from ${event.details || "team"} by ${actor} (${timestamp})`;
|
|
4315
|
+
case "pr_created":
|
|
4316
|
+
return `${arrow} PR created by ${actor} (${timestamp})`;
|
|
4317
|
+
case "pr_merged":
|
|
4318
|
+
return `${arrow} PR merged by ${actor} (${timestamp})`;
|
|
4319
|
+
default:
|
|
4320
|
+
return `${arrow} ${event.type} by ${actor} (${timestamp})`;
|
|
4321
|
+
}
|
|
4322
|
+
}
|
|
4323
|
+
function formatRelativeDate(date) {
|
|
4324
|
+
const now = /* @__PURE__ */ new Date();
|
|
4325
|
+
const diffMs = now.getTime() - date.getTime();
|
|
4326
|
+
const diffHours = Math.round(diffMs / (1e3 * 60 * 60));
|
|
4327
|
+
const dateStr = date.toLocaleDateString("en-US", {
|
|
4328
|
+
month: "short",
|
|
4329
|
+
day: "numeric",
|
|
4330
|
+
hour: "2-digit",
|
|
4331
|
+
minute: "2-digit"
|
|
4332
|
+
});
|
|
4333
|
+
if (diffHours < 24) {
|
|
4334
|
+
return `${diffHours}h ago (${dateStr})`;
|
|
4335
|
+
} else if (diffHours < 48) {
|
|
4336
|
+
return `yesterday (${dateStr})`;
|
|
4337
|
+
} else {
|
|
4338
|
+
const diffDays = Math.round(diffHours / 24);
|
|
4339
|
+
return `${diffDays} days ago (${dateStr})`;
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
function formatShortTimestamp(isoTimestamp) {
|
|
4343
|
+
if (!isoTimestamp) return "";
|
|
4344
|
+
const date = new Date(isoTimestamp);
|
|
4345
|
+
return date.toLocaleDateString("en-US", {
|
|
4346
|
+
month: "short",
|
|
4347
|
+
day: "numeric",
|
|
4348
|
+
hour: "2-digit",
|
|
4349
|
+
minute: "2-digit"
|
|
4350
|
+
});
|
|
4351
|
+
}
|
|
4352
|
+
|
|
3807
4353
|
// src/dashboard/index.ts
|
|
3808
4354
|
var import_child_process3 = require("child_process");
|
|
3809
4355
|
var import_util3 = require("util");
|
|
@@ -5261,6 +5807,7 @@ EOF
|
|
|
5261
5807
|
findSessionFile,
|
|
5262
5808
|
formatAction,
|
|
5263
5809
|
formatConflict,
|
|
5810
|
+
formatStandupText,
|
|
5264
5811
|
gatherDashboardData,
|
|
5265
5812
|
generateBranchName,
|
|
5266
5813
|
generateWorktreePath,
|
|
@@ -5318,6 +5865,7 @@ EOF
|
|
|
5318
5865
|
parseIssueUrl,
|
|
5319
5866
|
parseRateLimitDelay,
|
|
5320
5867
|
parseSessionLine,
|
|
5868
|
+
parseSince,
|
|
5321
5869
|
pullLatest,
|
|
5322
5870
|
queries,
|
|
5323
5871
|
registerAgent,
|