@bretwardjames/ghp-core 0.6.1 → 0.7.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/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,6 +192,7 @@ __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,
@@ -280,6 +283,7 @@ var PROJECT_ITEMS_QUERY = `
280
283
  number
281
284
  url
282
285
  state
286
+ updatedAt
283
287
  issueType { name }
284
288
  assignees(first: 5) { nodes { login } }
285
289
  labels(first: 10) { nodes { name color } }
@@ -294,6 +298,7 @@ var PROJECT_ITEMS_QUERY = `
294
298
  number
295
299
  url
296
300
  state
301
+ updatedAt
297
302
  merged
298
303
  assignees(first: 5) { nodes { login } }
299
304
  labels(first: 10) { nodes { name color } }
@@ -657,6 +662,112 @@ var ISSUE_WITH_PROJECT_ITEMS_QUERY = `
657
662
  }
658
663
  }
659
664
  `;
665
+ var ISSUE_TIMELINE_QUERY = `
666
+ query($owner: String!, $name: String!, $number: Int!, $since: DateTime!) {
667
+ repository(owner: $owner, name: $name) {
668
+ issueOrPullRequest(number: $number) {
669
+ ... on Issue {
670
+ timelineItems(first: 100, since: $since) {
671
+ nodes {
672
+ __typename
673
+ ... on IssueComment {
674
+ author { login }
675
+ createdAt
676
+ body
677
+ }
678
+ ... on LabeledEvent {
679
+ actor { login }
680
+ createdAt
681
+ label { name }
682
+ }
683
+ ... on UnlabeledEvent {
684
+ actor { login }
685
+ createdAt
686
+ label { name }
687
+ }
688
+ ... on AssignedEvent {
689
+ actor { login }
690
+ createdAt
691
+ assignee { ... on User { login } }
692
+ }
693
+ ... on UnassignedEvent {
694
+ actor { login }
695
+ createdAt
696
+ assignee { ... on User { login } }
697
+ }
698
+ ... on ClosedEvent {
699
+ actor { login }
700
+ createdAt
701
+ }
702
+ ... on ReopenedEvent {
703
+ actor { login }
704
+ createdAt
705
+ }
706
+ ... on CrossReferencedEvent {
707
+ actor { login }
708
+ createdAt
709
+ source {
710
+ __typename
711
+ ... on PullRequest { number title url }
712
+ ... on Issue { number title }
713
+ }
714
+ }
715
+ }
716
+ }
717
+ }
718
+ ... on PullRequest {
719
+ timelineItems(first: 100, since: $since) {
720
+ nodes {
721
+ __typename
722
+ ... on IssueComment {
723
+ author { login }
724
+ createdAt
725
+ body
726
+ }
727
+ ... on LabeledEvent {
728
+ actor { login }
729
+ createdAt
730
+ label { name }
731
+ }
732
+ ... on UnlabeledEvent {
733
+ actor { login }
734
+ createdAt
735
+ label { name }
736
+ }
737
+ ... on AssignedEvent {
738
+ actor { login }
739
+ createdAt
740
+ assignee { ... on User { login } }
741
+ }
742
+ ... on UnassignedEvent {
743
+ actor { login }
744
+ createdAt
745
+ assignee { ... on User { login } }
746
+ }
747
+ ... on ClosedEvent {
748
+ actor { login }
749
+ createdAt
750
+ }
751
+ ... on ReopenedEvent {
752
+ actor { login }
753
+ createdAt
754
+ }
755
+ ... on CrossReferencedEvent {
756
+ actor { login }
757
+ createdAt
758
+ source {
759
+ __typename
760
+ ... on PullRequest { number title url }
761
+ ... on Issue { number title }
762
+ }
763
+ }
764
+ }
765
+ }
766
+ }
767
+ }
768
+ }
769
+ }
770
+ `;
660
771
  var ISSUE_RELATIONSHIPS_QUERY = `
661
772
  query($owner: String!, $name: String!, $number: Int!) {
662
773
  repository(owner: $owner, name: $name) {
@@ -1062,7 +1173,8 @@ var GitHubAPI = class {
1062
1173
  parent,
1063
1174
  subIssues,
1064
1175
  blockedBy,
1065
- blocking
1176
+ blocking,
1177
+ updatedAt: content.updatedAt || null
1066
1178
  };
1067
1179
  });
1068
1180
  }
@@ -1171,7 +1283,8 @@ var GitHubAPI = class {
1171
1283
  parent: issue.parent,
1172
1284
  subIssues: issue.subIssues.nodes,
1173
1285
  blockedBy: issue.blockedBy.nodes,
1174
- blocking: issue.blocking.nodes
1286
+ blocking: issue.blocking.nodes,
1287
+ updatedAt: null
1175
1288
  };
1176
1289
  } catch (error) {
1177
1290
  return null;
@@ -1733,6 +1846,158 @@ var GitHubAPI = class {
1733
1846
  return null;
1734
1847
  }
1735
1848
  }
1849
+ /**
1850
+ * Get recent activity across all project items since a given time.
1851
+ * Uses a two-pass approach for efficiency:
1852
+ * Pass 1: Fetch all project items, filter by updatedAt client-side
1853
+ * Pass 2: Fetch timeline events only for items that changed
1854
+ */
1855
+ async getRecentActivity(repo, since, options) {
1856
+ if (!this.graphqlWithAuth) throw new Error("Not authenticated");
1857
+ const projects = await this.getProjects(repo);
1858
+ if (projects.length === 0) return [];
1859
+ const allItems = [];
1860
+ for (const project of projects) {
1861
+ const items = await this.getProjectItems(project.id, project.title);
1862
+ allItems.push(...items);
1863
+ }
1864
+ const sinceMs = since.getTime();
1865
+ const sinceISO = since.toISOString();
1866
+ let recentItems = allItems.filter(
1867
+ (item) => item.updatedAt && new Date(item.updatedAt).getTime() >= sinceMs
1868
+ );
1869
+ if (options?.mine && this.username) {
1870
+ recentItems = recentItems.filter(
1871
+ (item) => item.assignees.includes(this.username)
1872
+ );
1873
+ }
1874
+ const seen = /* @__PURE__ */ new Set();
1875
+ recentItems = recentItems.filter((item) => {
1876
+ const key = `${item.repository || ""}#${item.number}`;
1877
+ if (!item.number || seen.has(key)) return false;
1878
+ seen.add(key);
1879
+ return true;
1880
+ });
1881
+ const activities = [];
1882
+ const BATCH_SIZE = 5;
1883
+ for (let i = 0; i < recentItems.length; i += BATCH_SIZE) {
1884
+ const batch = recentItems.slice(i, i + BATCH_SIZE);
1885
+ const results = await Promise.allSettled(
1886
+ batch.map(async (item) => {
1887
+ if (!item.number || !item.repository) return null;
1888
+ const [owner, name] = item.repository.split("/");
1889
+ if (!owner || !name) return null;
1890
+ const events = await this.fetchTimelineEvents(
1891
+ owner,
1892
+ name,
1893
+ item.number,
1894
+ sinceISO
1895
+ );
1896
+ if (events.length === 0) return null;
1897
+ return {
1898
+ issue: {
1899
+ number: item.number,
1900
+ title: item.title,
1901
+ url: item.url || ""
1902
+ },
1903
+ status: item.status,
1904
+ assignees: item.assignees,
1905
+ changes: events
1906
+ };
1907
+ })
1908
+ );
1909
+ for (const result of results) {
1910
+ if (result.status === "fulfilled" && result.value) {
1911
+ activities.push(result.value);
1912
+ }
1913
+ }
1914
+ }
1915
+ activities.sort((a, b) => {
1916
+ const aLatest = a.changes[a.changes.length - 1]?.timestamp || "";
1917
+ const bLatest = b.changes[b.changes.length - 1]?.timestamp || "";
1918
+ return bLatest.localeCompare(aLatest);
1919
+ });
1920
+ return activities;
1921
+ }
1922
+ /**
1923
+ * Fetch and normalize timeline events for a single issue/PR
1924
+ */
1925
+ async fetchTimelineEvents(owner, name, number, since) {
1926
+ if (!this.graphqlWithAuth) throw new Error("Not authenticated");
1927
+ const response = await this.graphqlWithRetry(
1928
+ ISSUE_TIMELINE_QUERY,
1929
+ { owner, name, number, since }
1930
+ );
1931
+ const item = response.repository.issueOrPullRequest;
1932
+ if (!item) return [];
1933
+ const events = [];
1934
+ for (const node of item.timelineItems.nodes) {
1935
+ const actor = node.actor?.login || node.author?.login || "unknown";
1936
+ const timestamp = node.createdAt || "";
1937
+ switch (node.__typename) {
1938
+ case "IssueComment":
1939
+ events.push({
1940
+ type: "comment",
1941
+ actor,
1942
+ timestamp,
1943
+ details: node.body ? node.body.substring(0, 80) + (node.body.length > 80 ? "..." : "") : void 0
1944
+ });
1945
+ break;
1946
+ case "LabeledEvent":
1947
+ events.push({
1948
+ type: "labeled",
1949
+ actor,
1950
+ timestamp,
1951
+ details: node.label?.name
1952
+ });
1953
+ break;
1954
+ case "UnlabeledEvent":
1955
+ events.push({
1956
+ type: "unlabeled",
1957
+ actor,
1958
+ timestamp,
1959
+ details: node.label?.name
1960
+ });
1961
+ break;
1962
+ case "AssignedEvent":
1963
+ events.push({
1964
+ type: "assigned",
1965
+ actor,
1966
+ timestamp,
1967
+ details: node.assignee?.login
1968
+ });
1969
+ break;
1970
+ case "UnassignedEvent":
1971
+ events.push({
1972
+ type: "unassigned",
1973
+ actor,
1974
+ timestamp,
1975
+ details: node.assignee?.login
1976
+ });
1977
+ break;
1978
+ case "ClosedEvent":
1979
+ events.push({ type: "closed", actor, timestamp });
1980
+ break;
1981
+ case "ReopenedEvent":
1982
+ events.push({ type: "reopened", actor, timestamp });
1983
+ break;
1984
+ case "CrossReferencedEvent": {
1985
+ const source = node.source;
1986
+ if (source) {
1987
+ const ref = source.__typename === "PullRequest" ? `PR #${source.number}: ${source.title}` : `#${source.number}: ${source.title}`;
1988
+ events.push({
1989
+ type: "referenced",
1990
+ actor,
1991
+ timestamp,
1992
+ details: ref
1993
+ });
1994
+ }
1995
+ break;
1996
+ }
1997
+ }
1998
+ }
1999
+ return events;
2000
+ }
1736
2001
  };
1737
2002
 
1738
2003
  // src/branch-linker.ts
@@ -3804,6 +4069,106 @@ function validateUrl(url) {
3804
4069
  return url;
3805
4070
  }
3806
4071
 
4072
+ // src/standup.ts
4073
+ function formatStandupText(activities, options) {
4074
+ const { since } = options;
4075
+ const lines = [];
4076
+ const sinceStr = formatRelativeDate(since);
4077
+ const issueCount = activities.length;
4078
+ lines.push(`Since ${sinceStr} \u2014 ${issueCount} issue${issueCount !== 1 ? "s" : ""} changed`);
4079
+ lines.push("");
4080
+ if (activities.length === 0) {
4081
+ lines.push("No activity found in this time window.");
4082
+ return lines.join("\n");
4083
+ }
4084
+ for (const activity of activities) {
4085
+ const statusTag = activity.status ? ` [${activity.status}]` : "";
4086
+ lines.push(`#${activity.issue.number} ${activity.issue.title}${statusTag}`);
4087
+ for (const event of activity.changes) {
4088
+ lines.push(` ${formatEventLine(event)}`);
4089
+ }
4090
+ lines.push("");
4091
+ }
4092
+ return lines.join("\n").trimEnd();
4093
+ }
4094
+ function parseSince(input) {
4095
+ const isoDate = new Date(input);
4096
+ if (!isNaN(isoDate.getTime()) && input.includes("-")) {
4097
+ return isoDate;
4098
+ }
4099
+ const match = input.match(/^(\d+)\s*(h|d|w)$/i);
4100
+ if (!match) {
4101
+ throw new Error(`Invalid duration format: "${input}". Use formats like "24h", "2d", "1w", or an ISO date.`);
4102
+ }
4103
+ const amount = parseInt(match[1], 10);
4104
+ const unit = match[2].toLowerCase();
4105
+ const now = /* @__PURE__ */ new Date();
4106
+ switch (unit) {
4107
+ case "h":
4108
+ return new Date(now.getTime() - amount * 60 * 60 * 1e3);
4109
+ case "d":
4110
+ return new Date(now.getTime() - amount * 24 * 60 * 60 * 1e3);
4111
+ case "w":
4112
+ return new Date(now.getTime() - amount * 7 * 24 * 60 * 60 * 1e3);
4113
+ default:
4114
+ throw new Error(`Unknown duration unit: "${unit}"`);
4115
+ }
4116
+ }
4117
+ function formatEventLine(event) {
4118
+ const arrow = "\u2197";
4119
+ const timestamp = formatShortTimestamp(event.timestamp);
4120
+ const actor = event.actor;
4121
+ switch (event.type) {
4122
+ case "comment":
4123
+ return `${arrow} Comment by ${actor} (${timestamp})${event.details ? ": " + event.details : ""}`;
4124
+ case "labeled":
4125
+ return `${arrow} Labeled "${event.details}" by ${actor} (${timestamp})`;
4126
+ case "unlabeled":
4127
+ return `${arrow} Unlabeled "${event.details}" by ${actor} (${timestamp})`;
4128
+ case "assigned":
4129
+ return `${arrow} Assigned to ${event.details || actor} (${timestamp})`;
4130
+ case "unassigned":
4131
+ return `${arrow} Unassigned ${event.details || ""} by ${actor} (${timestamp})`;
4132
+ case "closed":
4133
+ return `${arrow} Closed by ${actor} (${timestamp})`;
4134
+ case "reopened":
4135
+ return `${arrow} Reopened by ${actor} (${timestamp})`;
4136
+ case "referenced":
4137
+ return `${arrow} ${event.details} linked by ${actor} (${timestamp})`;
4138
+ default:
4139
+ return `${arrow} ${event.type} by ${actor} (${timestamp})`;
4140
+ }
4141
+ }
4142
+ function formatRelativeDate(date) {
4143
+ const now = /* @__PURE__ */ new Date();
4144
+ const diffMs = now.getTime() - date.getTime();
4145
+ const diffHours = Math.round(diffMs / (1e3 * 60 * 60));
4146
+ const dateStr = date.toLocaleDateString("en-US", {
4147
+ month: "short",
4148
+ day: "numeric",
4149
+ hour: "2-digit",
4150
+ minute: "2-digit"
4151
+ });
4152
+ if (diffHours < 24) {
4153
+ return `${diffHours}h ago (${dateStr})`;
4154
+ } else if (diffHours < 48) {
4155
+ return `yesterday (${dateStr})`;
4156
+ } else {
4157
+ const diffDays = Math.round(diffHours / 24);
4158
+ return `${diffDays} days ago (${dateStr})`;
4159
+ }
4160
+ }
4161
+ function formatShortTimestamp(isoTimestamp) {
4162
+ if (!isoTimestamp) return "";
4163
+ const date = new Date(isoTimestamp);
4164
+ return date.toLocaleDateString("en-US", {
4165
+ month: "short",
4166
+ day: "numeric",
4167
+ hour: "2-digit",
4168
+ minute: "2-digit"
4169
+ });
4170
+ }
4171
+
3807
4172
  // src/dashboard/index.ts
3808
4173
  var import_child_process3 = require("child_process");
3809
4174
  var import_util3 = require("util");
@@ -5261,6 +5626,7 @@ EOF
5261
5626
  findSessionFile,
5262
5627
  formatAction,
5263
5628
  formatConflict,
5629
+ formatStandupText,
5264
5630
  gatherDashboardData,
5265
5631
  generateBranchName,
5266
5632
  generateWorktreePath,
@@ -5318,6 +5684,7 @@ EOF
5318
5684
  parseIssueUrl,
5319
5685
  parseRateLimitDelay,
5320
5686
  parseSessionLine,
5687
+ parseSince,
5321
5688
  pullLatest,
5322
5689
  queries,
5323
5690
  registerAgent,
package/dist/index.d.cts CHANGED
@@ -121,6 +121,7 @@ interface ProjectItem {
121
121
  subIssues: RelatedIssue[];
122
122
  blockedBy: BlockingIssue[];
123
123
  blocking: BlockingIssue[];
124
+ updatedAt: string | null;
124
125
  }
125
126
  /**
126
127
  * Status field information for a project
@@ -424,6 +425,28 @@ interface BlockingRelationships {
424
425
  /** Issues that this issue is blocking (these are blocked by this issue) */
425
426
  blocking: BlockingIssue[];
426
427
  }
428
+ /**
429
+ * Activity summary for a single issue/PR
430
+ */
431
+ interface IssueActivity {
432
+ issue: {
433
+ number: number;
434
+ title: string;
435
+ url: string;
436
+ };
437
+ status: string | null;
438
+ assignees: string[];
439
+ changes: ActivityEvent[];
440
+ }
441
+ /**
442
+ * A single activity event on an issue
443
+ */
444
+ interface ActivityEvent {
445
+ type: 'comment' | 'assigned' | 'unassigned' | 'labeled' | 'unlabeled' | 'closed' | 'reopened' | 'referenced';
446
+ actor: string;
447
+ timestamp: string;
448
+ details?: string;
449
+ }
427
450
 
428
451
  /**
429
452
  * GitHub API client for Projects V2.
@@ -658,6 +681,19 @@ declare class GitHubAPI {
658
681
  * Get issue relationships (parent, sub-issues, and blocking relationships)
659
682
  */
660
683
  getIssueRelationships(repo: RepoInfo, issueNumber: number): Promise<IssueRelationships | null>;
684
+ /**
685
+ * Get recent activity across all project items since a given time.
686
+ * Uses a two-pass approach for efficiency:
687
+ * Pass 1: Fetch all project items, filter by updatedAt client-side
688
+ * Pass 2: Fetch timeline events only for items that changed
689
+ */
690
+ getRecentActivity(repo: RepoInfo, since: Date, options?: {
691
+ mine?: boolean;
692
+ }): Promise<IssueActivity[]>;
693
+ /**
694
+ * Fetch and normalize timeline events for a single issue/PR
695
+ */
696
+ private fetchTimelineEvents;
661
697
  }
662
698
 
663
699
  /**
@@ -1110,7 +1146,7 @@ declare const REPOSITORY_ID_QUERY = "\n query($owner: String!, $name: String!
1110
1146
  /**
1111
1147
  * Query to get project items with all field values (paginated)
1112
1148
  */
1113
- declare const PROJECT_ITEMS_QUERY = "\n query($projectId: ID!, $cursor: String) {\n node(id: $projectId) {\n ... on ProjectV2 {\n items(first: 100, after: $cursor) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n fieldValues(first: 20) {\n nodes {\n __typename\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field { ... on ProjectV2SingleSelectField { name } }\n }\n ... on ProjectV2ItemFieldTextValue {\n text\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldNumberValue {\n number\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldDateValue {\n date\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldIterationValue {\n title\n field { ... on ProjectV2IterationField { name } }\n }\n }\n }\n content {\n __typename\n ... on Issue {\n title\n number\n url\n state\n issueType { name }\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n repository { name owner { login } }\n parent { id number title state }\n subIssues(first: 50) { nodes { id number title state } }\n blockedBy(first: 20) { nodes { id number title state } }\n blocking(first: 20) { nodes { id number title state } }\n }\n ... on PullRequest {\n title\n number\n url\n state\n merged\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n repository { name owner { login } }\n }\n ... on DraftIssue {\n title\n }\n }\n }\n }\n }\n }\n }\n";
1149
+ declare const PROJECT_ITEMS_QUERY = "\n query($projectId: ID!, $cursor: String) {\n node(id: $projectId) {\n ... on ProjectV2 {\n items(first: 100, after: $cursor) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n fieldValues(first: 20) {\n nodes {\n __typename\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field { ... on ProjectV2SingleSelectField { name } }\n }\n ... on ProjectV2ItemFieldTextValue {\n text\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldNumberValue {\n number\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldDateValue {\n date\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldIterationValue {\n title\n field { ... on ProjectV2IterationField { name } }\n }\n }\n }\n content {\n __typename\n ... on Issue {\n title\n number\n url\n state\n updatedAt\n issueType { name }\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n repository { name owner { login } }\n parent { id number title state }\n subIssues(first: 50) { nodes { id number title state } }\n blockedBy(first: 20) { nodes { id number title state } }\n blocking(first: 20) { nodes { id number title state } }\n }\n ... on PullRequest {\n title\n number\n url\n state\n updatedAt\n merged\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n repository { name owner { login } }\n }\n ... on DraftIssue {\n title\n }\n }\n }\n }\n }\n }\n }\n";
1114
1150
  /**
1115
1151
  * Query to get project fields (including status options)
1116
1152
  */
@@ -1215,6 +1251,11 @@ declare const REMOVE_BLOCKED_BY_MUTATION = "\n mutation($issueId: ID!, $block
1215
1251
  * Query to get issue with its project items (direct lookup, no pagination needed)
1216
1252
  */
1217
1253
  declare const ISSUE_WITH_PROJECT_ITEMS_QUERY = "\n query($owner: String!, $name: String!, $number: Int!) {\n repository(owner: $owner, name: $name) {\n issue(number: $number) {\n id\n title\n number\n url\n state\n issueType { name }\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n parent { id number title state }\n subIssues(first: 50) { nodes { id number title state } }\n blockedBy(first: 20) { nodes { id number title state } }\n blocking(first: 20) { nodes { id number title state } }\n projectItems(first: 10) {\n nodes {\n id\n project { id title number }\n fieldValues(first: 20) {\n nodes {\n __typename\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field { ... on ProjectV2SingleSelectField { name } }\n }\n ... on ProjectV2ItemFieldTextValue {\n text\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldNumberValue {\n number\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldDateValue {\n date\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldIterationValue {\n title\n field { ... on ProjectV2IterationField { name } }\n }\n }\n }\n }\n }\n }\n }\n }\n";
1254
+ /**
1255
+ * Query to get timeline events for an issue since a given time.
1256
+ * Used by the standup command to fetch specific activity details.
1257
+ */
1258
+ declare const ISSUE_TIMELINE_QUERY = "\n query($owner: String!, $name: String!, $number: Int!, $since: DateTime!) {\n repository(owner: $owner, name: $name) {\n issueOrPullRequest(number: $number) {\n ... on Issue {\n timelineItems(first: 100, since: $since) {\n nodes {\n __typename\n ... on IssueComment {\n author { login }\n createdAt\n body\n }\n ... on LabeledEvent {\n actor { login }\n createdAt\n label { name }\n }\n ... on UnlabeledEvent {\n actor { login }\n createdAt\n label { name }\n }\n ... on AssignedEvent {\n actor { login }\n createdAt\n assignee { ... on User { login } }\n }\n ... on UnassignedEvent {\n actor { login }\n createdAt\n assignee { ... on User { login } }\n }\n ... on ClosedEvent {\n actor { login }\n createdAt\n }\n ... on ReopenedEvent {\n actor { login }\n createdAt\n }\n ... on CrossReferencedEvent {\n actor { login }\n createdAt\n source {\n __typename\n ... on PullRequest { number title url }\n ... on Issue { number title }\n }\n }\n }\n }\n }\n ... on PullRequest {\n timelineItems(first: 100, since: $since) {\n nodes {\n __typename\n ... on IssueComment {\n author { login }\n createdAt\n body\n }\n ... on LabeledEvent {\n actor { login }\n createdAt\n label { name }\n }\n ... on UnlabeledEvent {\n actor { login }\n createdAt\n label { name }\n }\n ... on AssignedEvent {\n actor { login }\n createdAt\n assignee { ... on User { login } }\n }\n ... on UnassignedEvent {\n actor { login }\n createdAt\n assignee { ... on User { login } }\n }\n ... on ClosedEvent {\n actor { login }\n createdAt\n }\n ... on ReopenedEvent {\n actor { login }\n createdAt\n }\n ... on CrossReferencedEvent {\n actor { login }\n createdAt\n source {\n __typename\n ... on PullRequest { number title url }\n ... on Issue { number title }\n }\n }\n }\n }\n }\n }\n }\n }\n";
1218
1259
  /**
1219
1260
  * Query to get issue relationships (parent and sub-issues)
1220
1261
  */
@@ -1233,6 +1274,7 @@ declare const queries_ISSUE_DETAILS_QUERY: typeof ISSUE_DETAILS_QUERY;
1233
1274
  declare const queries_ISSUE_FOR_UPDATE_QUERY: typeof ISSUE_FOR_UPDATE_QUERY;
1234
1275
  declare const queries_ISSUE_NODE_ID_QUERY: typeof ISSUE_NODE_ID_QUERY;
1235
1276
  declare const queries_ISSUE_RELATIONSHIPS_QUERY: typeof ISSUE_RELATIONSHIPS_QUERY;
1277
+ declare const queries_ISSUE_TIMELINE_QUERY: typeof ISSUE_TIMELINE_QUERY;
1236
1278
  declare const queries_ISSUE_TYPES_QUERY: typeof ISSUE_TYPES_QUERY;
1237
1279
  declare const queries_ISSUE_WITH_PROJECT_ITEMS_QUERY: typeof ISSUE_WITH_PROJECT_ITEMS_QUERY;
1238
1280
  declare const queries_LABEL_EXISTS_QUERY: typeof LABEL_EXISTS_QUERY;
@@ -1252,7 +1294,7 @@ declare const queries_UPDATE_ITEM_FIELD_MUTATION: typeof UPDATE_ITEM_FIELD_MUTAT
1252
1294
  declare const queries_UPDATE_ITEM_STATUS_MUTATION: typeof UPDATE_ITEM_STATUS_MUTATION;
1253
1295
  declare const queries_VIEWER_QUERY: typeof VIEWER_QUERY;
1254
1296
  declare namespace queries {
1255
- export { queries_ADD_BLOCKED_BY_MUTATION as ADD_BLOCKED_BY_MUTATION, queries_ADD_COMMENT_MUTATION as ADD_COMMENT_MUTATION, queries_ADD_LABELS_MUTATION as ADD_LABELS_MUTATION, queries_ADD_SUB_ISSUE_MUTATION as ADD_SUB_ISSUE_MUTATION, queries_ADD_TO_PROJECT_MUTATION as ADD_TO_PROJECT_MUTATION, queries_COLLABORATORS_QUERY as COLLABORATORS_QUERY, queries_CREATE_ISSUE_MUTATION as CREATE_ISSUE_MUTATION, queries_ISSUES_WITH_LABEL_QUERY as ISSUES_WITH_LABEL_QUERY, queries_ISSUE_AND_LABEL_QUERY as ISSUE_AND_LABEL_QUERY, queries_ISSUE_DETAILS_QUERY as ISSUE_DETAILS_QUERY, queries_ISSUE_FOR_UPDATE_QUERY as ISSUE_FOR_UPDATE_QUERY, queries_ISSUE_NODE_ID_QUERY as ISSUE_NODE_ID_QUERY, queries_ISSUE_RELATIONSHIPS_QUERY as ISSUE_RELATIONSHIPS_QUERY, queries_ISSUE_TYPES_QUERY as ISSUE_TYPES_QUERY, queries_ISSUE_WITH_PROJECT_ITEMS_QUERY as ISSUE_WITH_PROJECT_ITEMS_QUERY, queries_LABEL_EXISTS_QUERY as LABEL_EXISTS_QUERY, queries_PROJECT_FIELDS_QUERY as PROJECT_FIELDS_QUERY, queries_PROJECT_ITEMS_QUERY as PROJECT_ITEMS_QUERY, queries_PROJECT_VIEWS_QUERY as PROJECT_VIEWS_QUERY, queries_RECENT_ISSUES_QUERY as RECENT_ISSUES_QUERY, queries_REMOVE_BLOCKED_BY_MUTATION as REMOVE_BLOCKED_BY_MUTATION, queries_REMOVE_LABELS_MUTATION as REMOVE_LABELS_MUTATION, queries_REMOVE_SUB_ISSUE_MUTATION as REMOVE_SUB_ISSUE_MUTATION, queries_REPOSITORY_ID_QUERY as REPOSITORY_ID_QUERY, queries_REPOSITORY_PROJECTS_QUERY as REPOSITORY_PROJECTS_QUERY, queries_UPDATE_ISSUE_BODY_MUTATION as UPDATE_ISSUE_BODY_MUTATION, queries_UPDATE_ISSUE_MUTATION as UPDATE_ISSUE_MUTATION, queries_UPDATE_ISSUE_TYPE_MUTATION as UPDATE_ISSUE_TYPE_MUTATION, queries_UPDATE_ITEM_FIELD_MUTATION as UPDATE_ITEM_FIELD_MUTATION, queries_UPDATE_ITEM_STATUS_MUTATION as UPDATE_ITEM_STATUS_MUTATION, queries_VIEWER_QUERY as VIEWER_QUERY };
1297
+ export { queries_ADD_BLOCKED_BY_MUTATION as ADD_BLOCKED_BY_MUTATION, queries_ADD_COMMENT_MUTATION as ADD_COMMENT_MUTATION, queries_ADD_LABELS_MUTATION as ADD_LABELS_MUTATION, queries_ADD_SUB_ISSUE_MUTATION as ADD_SUB_ISSUE_MUTATION, queries_ADD_TO_PROJECT_MUTATION as ADD_TO_PROJECT_MUTATION, queries_COLLABORATORS_QUERY as COLLABORATORS_QUERY, queries_CREATE_ISSUE_MUTATION as CREATE_ISSUE_MUTATION, queries_ISSUES_WITH_LABEL_QUERY as ISSUES_WITH_LABEL_QUERY, queries_ISSUE_AND_LABEL_QUERY as ISSUE_AND_LABEL_QUERY, queries_ISSUE_DETAILS_QUERY as ISSUE_DETAILS_QUERY, queries_ISSUE_FOR_UPDATE_QUERY as ISSUE_FOR_UPDATE_QUERY, queries_ISSUE_NODE_ID_QUERY as ISSUE_NODE_ID_QUERY, queries_ISSUE_RELATIONSHIPS_QUERY as ISSUE_RELATIONSHIPS_QUERY, queries_ISSUE_TIMELINE_QUERY as ISSUE_TIMELINE_QUERY, queries_ISSUE_TYPES_QUERY as ISSUE_TYPES_QUERY, queries_ISSUE_WITH_PROJECT_ITEMS_QUERY as ISSUE_WITH_PROJECT_ITEMS_QUERY, queries_LABEL_EXISTS_QUERY as LABEL_EXISTS_QUERY, queries_PROJECT_FIELDS_QUERY as PROJECT_FIELDS_QUERY, queries_PROJECT_ITEMS_QUERY as PROJECT_ITEMS_QUERY, queries_PROJECT_VIEWS_QUERY as PROJECT_VIEWS_QUERY, queries_RECENT_ISSUES_QUERY as RECENT_ISSUES_QUERY, queries_REMOVE_BLOCKED_BY_MUTATION as REMOVE_BLOCKED_BY_MUTATION, queries_REMOVE_LABELS_MUTATION as REMOVE_LABELS_MUTATION, queries_REMOVE_SUB_ISSUE_MUTATION as REMOVE_SUB_ISSUE_MUTATION, queries_REPOSITORY_ID_QUERY as REPOSITORY_ID_QUERY, queries_REPOSITORY_PROJECTS_QUERY as REPOSITORY_PROJECTS_QUERY, queries_UPDATE_ISSUE_BODY_MUTATION as UPDATE_ISSUE_BODY_MUTATION, queries_UPDATE_ISSUE_MUTATION as UPDATE_ISSUE_MUTATION, queries_UPDATE_ISSUE_TYPE_MUTATION as UPDATE_ISSUE_TYPE_MUTATION, queries_UPDATE_ITEM_FIELD_MUTATION as UPDATE_ITEM_FIELD_MUTATION, queries_UPDATE_ITEM_STATUS_MUTATION as UPDATE_ITEM_STATUS_MUTATION, queries_VIEWER_QUERY as VIEWER_QUERY };
1256
1298
  }
1257
1299
 
1258
1300
  /**
@@ -2207,6 +2249,33 @@ declare function validateSafeString(value: string, fieldName?: string, pattern?:
2207
2249
  */
2208
2250
  declare function validateUrl(url: string): string;
2209
2251
 
2252
+ /**
2253
+ * Standup formatting utilities.
2254
+ *
2255
+ * Provides shared formatting for standup activity summaries,
2256
+ * used by CLI, VS Code extension, and MCP tool.
2257
+ */
2258
+
2259
+ /**
2260
+ * Options for formatting standup output
2261
+ */
2262
+ interface FormatStandupOptions {
2263
+ since: Date;
2264
+ colorize?: boolean;
2265
+ }
2266
+ /**
2267
+ * Format a standup summary as human-readable text.
2268
+ *
2269
+ * @param activities - Activity data from GitHubAPI.getRecentActivity()
2270
+ * @param options - Formatting options
2271
+ * @returns Formatted text string
2272
+ */
2273
+ declare function formatStandupText(activities: IssueActivity[], options: FormatStandupOptions): string;
2274
+ /**
2275
+ * Parse a duration string like "24h", "8h", "2d" or an ISO date into a Date.
2276
+ */
2277
+ declare function parseSince(input: string): Date;
2278
+
2210
2279
  /**
2211
2280
  * Dashboard Hooks - Registration and management for external content providers
2212
2281
  *
@@ -3241,4 +3310,4 @@ declare function shouldAbort(results: HookResult[]): boolean;
3241
3310
  */
3242
3311
  declare function hasHooksForEvent(event: EventType): boolean;
3243
3312
 
3244
- export { type AgentInstance, type AgentRegistry, type AgentSessionStatus, type AgentStatus, type AgentSummary, type ApiKeyProvider, type AssigneeInfo, type AuthError, type BaseEventPayload, type BlockingIssue, type BlockingRelationships, type BranchDashboardData, BranchLinker, CLI_TO_VSCODE_MAP, ClaudeClient, type ClaudeClientOptions, type ClaudeResult, type ClaudeTool, type Collaborator, type Commit, type ConflictChoices, type ConflictResolution, type ContentBlock, type CreateIssueOptions, type CreateIssueResult, type CreatePROptions, type CreatePRResult, type CreateWorktreeOptions, type CreateWorktreeResult, DEFAULT_RETRY_CONFIG, DEFAULT_VALUES, type DashboardHook, type DashboardOptions, type DateFieldValue, type DiffStats, type EventHook, type EventHookSettings, type EventHooksConfig, type EventPayload, type EventType, type ExpandIssueOptions, type ExpandedIssue, type FieldInfo, type FieldValue, type FieldValueConnection, type FileChange, GHP_TOOLS, type GeneratePRDescriptionOptions, GitError, GitHubAPI, type GitHubAPIOptions, type GitOptions, type HookExecutionOptions, type HookExecutionResult, type HookExitCodes, type HookItem, type HookMode, type HookOutcome, type HookResponse, type HookResult, type HooksConfig, type IssueCreatedPayload, type IssueDetails, type IssueReference, type IssueRelationships, type IssueStartedPayload, type IterationFieldValue, type LabelInfo, type Message, type NumberFieldValue, type OnFailureBehavior, type PRInfo, type PermissionPrompt, type PlanEpicOptions, type PlanEpicResult, type PrCreatedPayload, type PrCreatingPayload, type PrMergedPayload, type PrePrPayload, type Project, type ProjectConfig, type ProjectConventions, type ProjectItem, type ProjectItemContent, type ProjectItemsQueryResponse, type ProjectV2, type ProjectV2Field, type ProjectV2Item, type ProjectV2View, type ProjectWithViews, type ProjectsQueryResponse, type RegisterAgentOptions, type RelatedIssue, type RemoveWorktreeOptions, type RemoveWorktreeResult, type RepoInfo, type ResolvedClaudeConfig, type ResolvedSettings, type RetryConfig, SETTING_DISPLAY_NAMES, SYNCABLE_KEYS, type SessionEvent, SessionWatcher, type SettingConflict, type SettingsDiff, type SettingsSource, type SingleSelectFieldValue, type StartIssueOptions, type StartIssueResult, type StatusField, type StreamCallbacks, type StreamErrorEvent, type StreamEvent, type StreamEventBase, type StreamMessageCompleteEvent, type StreamOptions, type StreamTextEvent, type StreamToolInputDeltaEvent, type StreamToolUseCompleteEvent, type StreamToolUseStartEvent, type SyncableSettingKey, type SyncableSettings, TOOL_NAMES, type TextFieldValue, type TokenProvider, type TokenUsage, type ToolContext, type ToolHandler, type ToolHandlers, type UpdateAgentOptions, VSCODE_TO_CLI_MAP, type IssueInfo as WorkflowIssueInfo, type WorkflowResult, type WorktreeInfo as WorkflowWorktreeInfo, type WorktreeCreatedPayload, type WorktreeInfo$1 as WorktreeInfo, type WorktreeRemovedPayload, addEventHook, addHook, branchExists, buildConventionsContext, buildIssueUrl, buildOrgProjectUrl, buildProjectUrl, buildPullRequestUrl, buildRepoUrl, calculateBackoffDelay, checkTmuxForPermission, checkoutBranch, index as claudePrompts, cleanupStaleAgents, computeSettingsDiff, createBranch, createIssueWorkflow, createPRWorkflow, createSessionWatcher, createWorktree, createWorktreeWorkflow, detectRepository, disableEventHook, disableHook, enableEventHook, enableHook, executeAllHooks, executeEventHook, executeHook, executeHooksForEvent, extractIssueNumberFromBranch, fetchOrigin, findSessionFile, formatAction, formatConflict, gatherDashboardData, generateBranchName, generateWorktreePath, getAgent, getAgentByIssue, getAgentSummaries, getAllBranches, getChangedFiles, getCommitHistory, getCommitsAhead, getCommitsBehind, getCurrentBranch$1 as getCurrentBranch, getCurrentBranch as getDashboardCurrentBranch, getDefaultBaseBranch, getDefaultBranch, getDiffStats, getDiffSummary, getEnabledEventHooks, getEnabledHooks, getEventHook, getEventHooks, getEventHooksConfigPath, getEventSettings, getFullDiff, getGitHubRepo, getHook, getHooks, getHooksByCategory, getHooksConfigPath, getHooksForEvent, getIssueReferenceText, getLocalBranches, getRegistryPath, getRemoteBranches, getRepositoryRoot, getTools, getValidEventTypes, getValidModes, getValidOnFailureBehaviors, getWorktreeForBranch, hasDifferences, hasHooksForEvent, hasUncommittedChanges, isGitRepository, isTransientError, listAgents, listWorktrees, loadEventHooksConfig, loadHooksConfig, loadProjectConventions, loadRegistry, normalizeVSCodeSettings, parseBranchLink, parseGitHubUrl, parseIssueUrl, parseRateLimitDelay, parseSessionLine, pullLatest, queries, registerAgent, removeBranchLinkFromBody, removeEventHook, removeHook, removeWorktree, removeWorktreeWorkflow, resolveConflicts, sanitizeForBranchName, saveEventHooksConfig, saveHooksConfig, saveRegistry, setBranchLinkInBody, shellEscape, shouldAbort, skip, startIssueWorkflow, substituteTemplateVariables, toVSCodeSettings, unregisterAgent, updateAgent, updateEventHook, updateHook, useCli, useCustom, useVSCode, validateNumericInput, validateSafeString, validateUrl, withRetry, worktreeExists, wrapWithRetry };
3313
+ export { type ActivityEvent, type AgentInstance, type AgentRegistry, type AgentSessionStatus, type AgentStatus, type AgentSummary, type ApiKeyProvider, type AssigneeInfo, type AuthError, type BaseEventPayload, type BlockingIssue, type BlockingRelationships, type BranchDashboardData, BranchLinker, CLI_TO_VSCODE_MAP, ClaudeClient, type ClaudeClientOptions, type ClaudeResult, type ClaudeTool, type Collaborator, type Commit, type ConflictChoices, type ConflictResolution, type ContentBlock, type CreateIssueOptions, type CreateIssueResult, type CreatePROptions, type CreatePRResult, type CreateWorktreeOptions, type CreateWorktreeResult, DEFAULT_RETRY_CONFIG, DEFAULT_VALUES, type DashboardHook, type DashboardOptions, type DateFieldValue, type DiffStats, type EventHook, type EventHookSettings, type EventHooksConfig, type EventPayload, type EventType, type ExpandIssueOptions, type ExpandedIssue, type FieldInfo, type FieldValue, type FieldValueConnection, type FileChange, type FormatStandupOptions, GHP_TOOLS, type GeneratePRDescriptionOptions, GitError, GitHubAPI, type GitHubAPIOptions, type GitOptions, type HookExecutionOptions, type HookExecutionResult, type HookExitCodes, type HookItem, type HookMode, type HookOutcome, type HookResponse, type HookResult, type HooksConfig, type IssueActivity, type IssueCreatedPayload, type IssueDetails, type IssueReference, type IssueRelationships, type IssueStartedPayload, type IterationFieldValue, type LabelInfo, type Message, type NumberFieldValue, type OnFailureBehavior, type PRInfo, type PermissionPrompt, type PlanEpicOptions, type PlanEpicResult, type PrCreatedPayload, type PrCreatingPayload, type PrMergedPayload, type PrePrPayload, type Project, type ProjectConfig, type ProjectConventions, type ProjectItem, type ProjectItemContent, type ProjectItemsQueryResponse, type ProjectV2, type ProjectV2Field, type ProjectV2Item, type ProjectV2View, type ProjectWithViews, type ProjectsQueryResponse, type RegisterAgentOptions, type RelatedIssue, type RemoveWorktreeOptions, type RemoveWorktreeResult, type RepoInfo, type ResolvedClaudeConfig, type ResolvedSettings, type RetryConfig, SETTING_DISPLAY_NAMES, SYNCABLE_KEYS, type SessionEvent, SessionWatcher, type SettingConflict, type SettingsDiff, type SettingsSource, type SingleSelectFieldValue, type StartIssueOptions, type StartIssueResult, type StatusField, type StreamCallbacks, type StreamErrorEvent, type StreamEvent, type StreamEventBase, type StreamMessageCompleteEvent, type StreamOptions, type StreamTextEvent, type StreamToolInputDeltaEvent, type StreamToolUseCompleteEvent, type StreamToolUseStartEvent, type SyncableSettingKey, type SyncableSettings, TOOL_NAMES, type TextFieldValue, type TokenProvider, type TokenUsage, type ToolContext, type ToolHandler, type ToolHandlers, type UpdateAgentOptions, VSCODE_TO_CLI_MAP, type IssueInfo as WorkflowIssueInfo, type WorkflowResult, type WorktreeInfo as WorkflowWorktreeInfo, type WorktreeCreatedPayload, type WorktreeInfo$1 as WorktreeInfo, type WorktreeRemovedPayload, addEventHook, addHook, branchExists, buildConventionsContext, buildIssueUrl, buildOrgProjectUrl, buildProjectUrl, buildPullRequestUrl, buildRepoUrl, calculateBackoffDelay, checkTmuxForPermission, checkoutBranch, index as claudePrompts, cleanupStaleAgents, computeSettingsDiff, createBranch, createIssueWorkflow, createPRWorkflow, createSessionWatcher, createWorktree, createWorktreeWorkflow, detectRepository, disableEventHook, disableHook, enableEventHook, enableHook, executeAllHooks, executeEventHook, executeHook, executeHooksForEvent, extractIssueNumberFromBranch, fetchOrigin, findSessionFile, formatAction, formatConflict, formatStandupText, gatherDashboardData, generateBranchName, generateWorktreePath, getAgent, getAgentByIssue, getAgentSummaries, getAllBranches, getChangedFiles, getCommitHistory, getCommitsAhead, getCommitsBehind, getCurrentBranch$1 as getCurrentBranch, getCurrentBranch as getDashboardCurrentBranch, getDefaultBaseBranch, getDefaultBranch, getDiffStats, getDiffSummary, getEnabledEventHooks, getEnabledHooks, getEventHook, getEventHooks, getEventHooksConfigPath, getEventSettings, getFullDiff, getGitHubRepo, getHook, getHooks, getHooksByCategory, getHooksConfigPath, getHooksForEvent, getIssueReferenceText, getLocalBranches, getRegistryPath, getRemoteBranches, getRepositoryRoot, getTools, getValidEventTypes, getValidModes, getValidOnFailureBehaviors, getWorktreeForBranch, hasDifferences, hasHooksForEvent, hasUncommittedChanges, isGitRepository, isTransientError, listAgents, listWorktrees, loadEventHooksConfig, loadHooksConfig, loadProjectConventions, loadRegistry, normalizeVSCodeSettings, parseBranchLink, parseGitHubUrl, parseIssueUrl, parseRateLimitDelay, parseSessionLine, parseSince, pullLatest, queries, registerAgent, removeBranchLinkFromBody, removeEventHook, removeHook, removeWorktree, removeWorktreeWorkflow, resolveConflicts, sanitizeForBranchName, saveEventHooksConfig, saveHooksConfig, saveRegistry, setBranchLinkInBody, shellEscape, shouldAbort, skip, startIssueWorkflow, substituteTemplateVariables, toVSCodeSettings, unregisterAgent, updateAgent, updateEventHook, updateHook, useCli, useCustom, useVSCode, validateNumericInput, validateSafeString, validateUrl, withRetry, worktreeExists, wrapWithRetry };
package/dist/index.d.ts CHANGED
@@ -121,6 +121,7 @@ interface ProjectItem {
121
121
  subIssues: RelatedIssue[];
122
122
  blockedBy: BlockingIssue[];
123
123
  blocking: BlockingIssue[];
124
+ updatedAt: string | null;
124
125
  }
125
126
  /**
126
127
  * Status field information for a project
@@ -424,6 +425,28 @@ interface BlockingRelationships {
424
425
  /** Issues that this issue is blocking (these are blocked by this issue) */
425
426
  blocking: BlockingIssue[];
426
427
  }
428
+ /**
429
+ * Activity summary for a single issue/PR
430
+ */
431
+ interface IssueActivity {
432
+ issue: {
433
+ number: number;
434
+ title: string;
435
+ url: string;
436
+ };
437
+ status: string | null;
438
+ assignees: string[];
439
+ changes: ActivityEvent[];
440
+ }
441
+ /**
442
+ * A single activity event on an issue
443
+ */
444
+ interface ActivityEvent {
445
+ type: 'comment' | 'assigned' | 'unassigned' | 'labeled' | 'unlabeled' | 'closed' | 'reopened' | 'referenced';
446
+ actor: string;
447
+ timestamp: string;
448
+ details?: string;
449
+ }
427
450
 
428
451
  /**
429
452
  * GitHub API client for Projects V2.
@@ -658,6 +681,19 @@ declare class GitHubAPI {
658
681
  * Get issue relationships (parent, sub-issues, and blocking relationships)
659
682
  */
660
683
  getIssueRelationships(repo: RepoInfo, issueNumber: number): Promise<IssueRelationships | null>;
684
+ /**
685
+ * Get recent activity across all project items since a given time.
686
+ * Uses a two-pass approach for efficiency:
687
+ * Pass 1: Fetch all project items, filter by updatedAt client-side
688
+ * Pass 2: Fetch timeline events only for items that changed
689
+ */
690
+ getRecentActivity(repo: RepoInfo, since: Date, options?: {
691
+ mine?: boolean;
692
+ }): Promise<IssueActivity[]>;
693
+ /**
694
+ * Fetch and normalize timeline events for a single issue/PR
695
+ */
696
+ private fetchTimelineEvents;
661
697
  }
662
698
 
663
699
  /**
@@ -1110,7 +1146,7 @@ declare const REPOSITORY_ID_QUERY = "\n query($owner: String!, $name: String!
1110
1146
  /**
1111
1147
  * Query to get project items with all field values (paginated)
1112
1148
  */
1113
- declare const PROJECT_ITEMS_QUERY = "\n query($projectId: ID!, $cursor: String) {\n node(id: $projectId) {\n ... on ProjectV2 {\n items(first: 100, after: $cursor) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n fieldValues(first: 20) {\n nodes {\n __typename\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field { ... on ProjectV2SingleSelectField { name } }\n }\n ... on ProjectV2ItemFieldTextValue {\n text\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldNumberValue {\n number\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldDateValue {\n date\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldIterationValue {\n title\n field { ... on ProjectV2IterationField { name } }\n }\n }\n }\n content {\n __typename\n ... on Issue {\n title\n number\n url\n state\n issueType { name }\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n repository { name owner { login } }\n parent { id number title state }\n subIssues(first: 50) { nodes { id number title state } }\n blockedBy(first: 20) { nodes { id number title state } }\n blocking(first: 20) { nodes { id number title state } }\n }\n ... on PullRequest {\n title\n number\n url\n state\n merged\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n repository { name owner { login } }\n }\n ... on DraftIssue {\n title\n }\n }\n }\n }\n }\n }\n }\n";
1149
+ declare const PROJECT_ITEMS_QUERY = "\n query($projectId: ID!, $cursor: String) {\n node(id: $projectId) {\n ... on ProjectV2 {\n items(first: 100, after: $cursor) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n fieldValues(first: 20) {\n nodes {\n __typename\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field { ... on ProjectV2SingleSelectField { name } }\n }\n ... on ProjectV2ItemFieldTextValue {\n text\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldNumberValue {\n number\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldDateValue {\n date\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldIterationValue {\n title\n field { ... on ProjectV2IterationField { name } }\n }\n }\n }\n content {\n __typename\n ... on Issue {\n title\n number\n url\n state\n updatedAt\n issueType { name }\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n repository { name owner { login } }\n parent { id number title state }\n subIssues(first: 50) { nodes { id number title state } }\n blockedBy(first: 20) { nodes { id number title state } }\n blocking(first: 20) { nodes { id number title state } }\n }\n ... on PullRequest {\n title\n number\n url\n state\n updatedAt\n merged\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n repository { name owner { login } }\n }\n ... on DraftIssue {\n title\n }\n }\n }\n }\n }\n }\n }\n";
1114
1150
  /**
1115
1151
  * Query to get project fields (including status options)
1116
1152
  */
@@ -1215,6 +1251,11 @@ declare const REMOVE_BLOCKED_BY_MUTATION = "\n mutation($issueId: ID!, $block
1215
1251
  * Query to get issue with its project items (direct lookup, no pagination needed)
1216
1252
  */
1217
1253
  declare const ISSUE_WITH_PROJECT_ITEMS_QUERY = "\n query($owner: String!, $name: String!, $number: Int!) {\n repository(owner: $owner, name: $name) {\n issue(number: $number) {\n id\n title\n number\n url\n state\n issueType { name }\n assignees(first: 5) { nodes { login } }\n labels(first: 10) { nodes { name color } }\n parent { id number title state }\n subIssues(first: 50) { nodes { id number title state } }\n blockedBy(first: 20) { nodes { id number title state } }\n blocking(first: 20) { nodes { id number title state } }\n projectItems(first: 10) {\n nodes {\n id\n project { id title number }\n fieldValues(first: 20) {\n nodes {\n __typename\n ... on ProjectV2ItemFieldSingleSelectValue {\n name\n field { ... on ProjectV2SingleSelectField { name } }\n }\n ... on ProjectV2ItemFieldTextValue {\n text\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldNumberValue {\n number\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldDateValue {\n date\n field { ... on ProjectV2Field { name } }\n }\n ... on ProjectV2ItemFieldIterationValue {\n title\n field { ... on ProjectV2IterationField { name } }\n }\n }\n }\n }\n }\n }\n }\n }\n";
1254
+ /**
1255
+ * Query to get timeline events for an issue since a given time.
1256
+ * Used by the standup command to fetch specific activity details.
1257
+ */
1258
+ declare const ISSUE_TIMELINE_QUERY = "\n query($owner: String!, $name: String!, $number: Int!, $since: DateTime!) {\n repository(owner: $owner, name: $name) {\n issueOrPullRequest(number: $number) {\n ... on Issue {\n timelineItems(first: 100, since: $since) {\n nodes {\n __typename\n ... on IssueComment {\n author { login }\n createdAt\n body\n }\n ... on LabeledEvent {\n actor { login }\n createdAt\n label { name }\n }\n ... on UnlabeledEvent {\n actor { login }\n createdAt\n label { name }\n }\n ... on AssignedEvent {\n actor { login }\n createdAt\n assignee { ... on User { login } }\n }\n ... on UnassignedEvent {\n actor { login }\n createdAt\n assignee { ... on User { login } }\n }\n ... on ClosedEvent {\n actor { login }\n createdAt\n }\n ... on ReopenedEvent {\n actor { login }\n createdAt\n }\n ... on CrossReferencedEvent {\n actor { login }\n createdAt\n source {\n __typename\n ... on PullRequest { number title url }\n ... on Issue { number title }\n }\n }\n }\n }\n }\n ... on PullRequest {\n timelineItems(first: 100, since: $since) {\n nodes {\n __typename\n ... on IssueComment {\n author { login }\n createdAt\n body\n }\n ... on LabeledEvent {\n actor { login }\n createdAt\n label { name }\n }\n ... on UnlabeledEvent {\n actor { login }\n createdAt\n label { name }\n }\n ... on AssignedEvent {\n actor { login }\n createdAt\n assignee { ... on User { login } }\n }\n ... on UnassignedEvent {\n actor { login }\n createdAt\n assignee { ... on User { login } }\n }\n ... on ClosedEvent {\n actor { login }\n createdAt\n }\n ... on ReopenedEvent {\n actor { login }\n createdAt\n }\n ... on CrossReferencedEvent {\n actor { login }\n createdAt\n source {\n __typename\n ... on PullRequest { number title url }\n ... on Issue { number title }\n }\n }\n }\n }\n }\n }\n }\n }\n";
1218
1259
  /**
1219
1260
  * Query to get issue relationships (parent and sub-issues)
1220
1261
  */
@@ -1233,6 +1274,7 @@ declare const queries_ISSUE_DETAILS_QUERY: typeof ISSUE_DETAILS_QUERY;
1233
1274
  declare const queries_ISSUE_FOR_UPDATE_QUERY: typeof ISSUE_FOR_UPDATE_QUERY;
1234
1275
  declare const queries_ISSUE_NODE_ID_QUERY: typeof ISSUE_NODE_ID_QUERY;
1235
1276
  declare const queries_ISSUE_RELATIONSHIPS_QUERY: typeof ISSUE_RELATIONSHIPS_QUERY;
1277
+ declare const queries_ISSUE_TIMELINE_QUERY: typeof ISSUE_TIMELINE_QUERY;
1236
1278
  declare const queries_ISSUE_TYPES_QUERY: typeof ISSUE_TYPES_QUERY;
1237
1279
  declare const queries_ISSUE_WITH_PROJECT_ITEMS_QUERY: typeof ISSUE_WITH_PROJECT_ITEMS_QUERY;
1238
1280
  declare const queries_LABEL_EXISTS_QUERY: typeof LABEL_EXISTS_QUERY;
@@ -1252,7 +1294,7 @@ declare const queries_UPDATE_ITEM_FIELD_MUTATION: typeof UPDATE_ITEM_FIELD_MUTAT
1252
1294
  declare const queries_UPDATE_ITEM_STATUS_MUTATION: typeof UPDATE_ITEM_STATUS_MUTATION;
1253
1295
  declare const queries_VIEWER_QUERY: typeof VIEWER_QUERY;
1254
1296
  declare namespace queries {
1255
- export { queries_ADD_BLOCKED_BY_MUTATION as ADD_BLOCKED_BY_MUTATION, queries_ADD_COMMENT_MUTATION as ADD_COMMENT_MUTATION, queries_ADD_LABELS_MUTATION as ADD_LABELS_MUTATION, queries_ADD_SUB_ISSUE_MUTATION as ADD_SUB_ISSUE_MUTATION, queries_ADD_TO_PROJECT_MUTATION as ADD_TO_PROJECT_MUTATION, queries_COLLABORATORS_QUERY as COLLABORATORS_QUERY, queries_CREATE_ISSUE_MUTATION as CREATE_ISSUE_MUTATION, queries_ISSUES_WITH_LABEL_QUERY as ISSUES_WITH_LABEL_QUERY, queries_ISSUE_AND_LABEL_QUERY as ISSUE_AND_LABEL_QUERY, queries_ISSUE_DETAILS_QUERY as ISSUE_DETAILS_QUERY, queries_ISSUE_FOR_UPDATE_QUERY as ISSUE_FOR_UPDATE_QUERY, queries_ISSUE_NODE_ID_QUERY as ISSUE_NODE_ID_QUERY, queries_ISSUE_RELATIONSHIPS_QUERY as ISSUE_RELATIONSHIPS_QUERY, queries_ISSUE_TYPES_QUERY as ISSUE_TYPES_QUERY, queries_ISSUE_WITH_PROJECT_ITEMS_QUERY as ISSUE_WITH_PROJECT_ITEMS_QUERY, queries_LABEL_EXISTS_QUERY as LABEL_EXISTS_QUERY, queries_PROJECT_FIELDS_QUERY as PROJECT_FIELDS_QUERY, queries_PROJECT_ITEMS_QUERY as PROJECT_ITEMS_QUERY, queries_PROJECT_VIEWS_QUERY as PROJECT_VIEWS_QUERY, queries_RECENT_ISSUES_QUERY as RECENT_ISSUES_QUERY, queries_REMOVE_BLOCKED_BY_MUTATION as REMOVE_BLOCKED_BY_MUTATION, queries_REMOVE_LABELS_MUTATION as REMOVE_LABELS_MUTATION, queries_REMOVE_SUB_ISSUE_MUTATION as REMOVE_SUB_ISSUE_MUTATION, queries_REPOSITORY_ID_QUERY as REPOSITORY_ID_QUERY, queries_REPOSITORY_PROJECTS_QUERY as REPOSITORY_PROJECTS_QUERY, queries_UPDATE_ISSUE_BODY_MUTATION as UPDATE_ISSUE_BODY_MUTATION, queries_UPDATE_ISSUE_MUTATION as UPDATE_ISSUE_MUTATION, queries_UPDATE_ISSUE_TYPE_MUTATION as UPDATE_ISSUE_TYPE_MUTATION, queries_UPDATE_ITEM_FIELD_MUTATION as UPDATE_ITEM_FIELD_MUTATION, queries_UPDATE_ITEM_STATUS_MUTATION as UPDATE_ITEM_STATUS_MUTATION, queries_VIEWER_QUERY as VIEWER_QUERY };
1297
+ export { queries_ADD_BLOCKED_BY_MUTATION as ADD_BLOCKED_BY_MUTATION, queries_ADD_COMMENT_MUTATION as ADD_COMMENT_MUTATION, queries_ADD_LABELS_MUTATION as ADD_LABELS_MUTATION, queries_ADD_SUB_ISSUE_MUTATION as ADD_SUB_ISSUE_MUTATION, queries_ADD_TO_PROJECT_MUTATION as ADD_TO_PROJECT_MUTATION, queries_COLLABORATORS_QUERY as COLLABORATORS_QUERY, queries_CREATE_ISSUE_MUTATION as CREATE_ISSUE_MUTATION, queries_ISSUES_WITH_LABEL_QUERY as ISSUES_WITH_LABEL_QUERY, queries_ISSUE_AND_LABEL_QUERY as ISSUE_AND_LABEL_QUERY, queries_ISSUE_DETAILS_QUERY as ISSUE_DETAILS_QUERY, queries_ISSUE_FOR_UPDATE_QUERY as ISSUE_FOR_UPDATE_QUERY, queries_ISSUE_NODE_ID_QUERY as ISSUE_NODE_ID_QUERY, queries_ISSUE_RELATIONSHIPS_QUERY as ISSUE_RELATIONSHIPS_QUERY, queries_ISSUE_TIMELINE_QUERY as ISSUE_TIMELINE_QUERY, queries_ISSUE_TYPES_QUERY as ISSUE_TYPES_QUERY, queries_ISSUE_WITH_PROJECT_ITEMS_QUERY as ISSUE_WITH_PROJECT_ITEMS_QUERY, queries_LABEL_EXISTS_QUERY as LABEL_EXISTS_QUERY, queries_PROJECT_FIELDS_QUERY as PROJECT_FIELDS_QUERY, queries_PROJECT_ITEMS_QUERY as PROJECT_ITEMS_QUERY, queries_PROJECT_VIEWS_QUERY as PROJECT_VIEWS_QUERY, queries_RECENT_ISSUES_QUERY as RECENT_ISSUES_QUERY, queries_REMOVE_BLOCKED_BY_MUTATION as REMOVE_BLOCKED_BY_MUTATION, queries_REMOVE_LABELS_MUTATION as REMOVE_LABELS_MUTATION, queries_REMOVE_SUB_ISSUE_MUTATION as REMOVE_SUB_ISSUE_MUTATION, queries_REPOSITORY_ID_QUERY as REPOSITORY_ID_QUERY, queries_REPOSITORY_PROJECTS_QUERY as REPOSITORY_PROJECTS_QUERY, queries_UPDATE_ISSUE_BODY_MUTATION as UPDATE_ISSUE_BODY_MUTATION, queries_UPDATE_ISSUE_MUTATION as UPDATE_ISSUE_MUTATION, queries_UPDATE_ISSUE_TYPE_MUTATION as UPDATE_ISSUE_TYPE_MUTATION, queries_UPDATE_ITEM_FIELD_MUTATION as UPDATE_ITEM_FIELD_MUTATION, queries_UPDATE_ITEM_STATUS_MUTATION as UPDATE_ITEM_STATUS_MUTATION, queries_VIEWER_QUERY as VIEWER_QUERY };
1256
1298
  }
1257
1299
 
1258
1300
  /**
@@ -2207,6 +2249,33 @@ declare function validateSafeString(value: string, fieldName?: string, pattern?:
2207
2249
  */
2208
2250
  declare function validateUrl(url: string): string;
2209
2251
 
2252
+ /**
2253
+ * Standup formatting utilities.
2254
+ *
2255
+ * Provides shared formatting for standup activity summaries,
2256
+ * used by CLI, VS Code extension, and MCP tool.
2257
+ */
2258
+
2259
+ /**
2260
+ * Options for formatting standup output
2261
+ */
2262
+ interface FormatStandupOptions {
2263
+ since: Date;
2264
+ colorize?: boolean;
2265
+ }
2266
+ /**
2267
+ * Format a standup summary as human-readable text.
2268
+ *
2269
+ * @param activities - Activity data from GitHubAPI.getRecentActivity()
2270
+ * @param options - Formatting options
2271
+ * @returns Formatted text string
2272
+ */
2273
+ declare function formatStandupText(activities: IssueActivity[], options: FormatStandupOptions): string;
2274
+ /**
2275
+ * Parse a duration string like "24h", "8h", "2d" or an ISO date into a Date.
2276
+ */
2277
+ declare function parseSince(input: string): Date;
2278
+
2210
2279
  /**
2211
2280
  * Dashboard Hooks - Registration and management for external content providers
2212
2281
  *
@@ -3241,4 +3310,4 @@ declare function shouldAbort(results: HookResult[]): boolean;
3241
3310
  */
3242
3311
  declare function hasHooksForEvent(event: EventType): boolean;
3243
3312
 
3244
- export { type AgentInstance, type AgentRegistry, type AgentSessionStatus, type AgentStatus, type AgentSummary, type ApiKeyProvider, type AssigneeInfo, type AuthError, type BaseEventPayload, type BlockingIssue, type BlockingRelationships, type BranchDashboardData, BranchLinker, CLI_TO_VSCODE_MAP, ClaudeClient, type ClaudeClientOptions, type ClaudeResult, type ClaudeTool, type Collaborator, type Commit, type ConflictChoices, type ConflictResolution, type ContentBlock, type CreateIssueOptions, type CreateIssueResult, type CreatePROptions, type CreatePRResult, type CreateWorktreeOptions, type CreateWorktreeResult, DEFAULT_RETRY_CONFIG, DEFAULT_VALUES, type DashboardHook, type DashboardOptions, type DateFieldValue, type DiffStats, type EventHook, type EventHookSettings, type EventHooksConfig, type EventPayload, type EventType, type ExpandIssueOptions, type ExpandedIssue, type FieldInfo, type FieldValue, type FieldValueConnection, type FileChange, GHP_TOOLS, type GeneratePRDescriptionOptions, GitError, GitHubAPI, type GitHubAPIOptions, type GitOptions, type HookExecutionOptions, type HookExecutionResult, type HookExitCodes, type HookItem, type HookMode, type HookOutcome, type HookResponse, type HookResult, type HooksConfig, type IssueCreatedPayload, type IssueDetails, type IssueReference, type IssueRelationships, type IssueStartedPayload, type IterationFieldValue, type LabelInfo, type Message, type NumberFieldValue, type OnFailureBehavior, type PRInfo, type PermissionPrompt, type PlanEpicOptions, type PlanEpicResult, type PrCreatedPayload, type PrCreatingPayload, type PrMergedPayload, type PrePrPayload, type Project, type ProjectConfig, type ProjectConventions, type ProjectItem, type ProjectItemContent, type ProjectItemsQueryResponse, type ProjectV2, type ProjectV2Field, type ProjectV2Item, type ProjectV2View, type ProjectWithViews, type ProjectsQueryResponse, type RegisterAgentOptions, type RelatedIssue, type RemoveWorktreeOptions, type RemoveWorktreeResult, type RepoInfo, type ResolvedClaudeConfig, type ResolvedSettings, type RetryConfig, SETTING_DISPLAY_NAMES, SYNCABLE_KEYS, type SessionEvent, SessionWatcher, type SettingConflict, type SettingsDiff, type SettingsSource, type SingleSelectFieldValue, type StartIssueOptions, type StartIssueResult, type StatusField, type StreamCallbacks, type StreamErrorEvent, type StreamEvent, type StreamEventBase, type StreamMessageCompleteEvent, type StreamOptions, type StreamTextEvent, type StreamToolInputDeltaEvent, type StreamToolUseCompleteEvent, type StreamToolUseStartEvent, type SyncableSettingKey, type SyncableSettings, TOOL_NAMES, type TextFieldValue, type TokenProvider, type TokenUsage, type ToolContext, type ToolHandler, type ToolHandlers, type UpdateAgentOptions, VSCODE_TO_CLI_MAP, type IssueInfo as WorkflowIssueInfo, type WorkflowResult, type WorktreeInfo as WorkflowWorktreeInfo, type WorktreeCreatedPayload, type WorktreeInfo$1 as WorktreeInfo, type WorktreeRemovedPayload, addEventHook, addHook, branchExists, buildConventionsContext, buildIssueUrl, buildOrgProjectUrl, buildProjectUrl, buildPullRequestUrl, buildRepoUrl, calculateBackoffDelay, checkTmuxForPermission, checkoutBranch, index as claudePrompts, cleanupStaleAgents, computeSettingsDiff, createBranch, createIssueWorkflow, createPRWorkflow, createSessionWatcher, createWorktree, createWorktreeWorkflow, detectRepository, disableEventHook, disableHook, enableEventHook, enableHook, executeAllHooks, executeEventHook, executeHook, executeHooksForEvent, extractIssueNumberFromBranch, fetchOrigin, findSessionFile, formatAction, formatConflict, gatherDashboardData, generateBranchName, generateWorktreePath, getAgent, getAgentByIssue, getAgentSummaries, getAllBranches, getChangedFiles, getCommitHistory, getCommitsAhead, getCommitsBehind, getCurrentBranch$1 as getCurrentBranch, getCurrentBranch as getDashboardCurrentBranch, getDefaultBaseBranch, getDefaultBranch, getDiffStats, getDiffSummary, getEnabledEventHooks, getEnabledHooks, getEventHook, getEventHooks, getEventHooksConfigPath, getEventSettings, getFullDiff, getGitHubRepo, getHook, getHooks, getHooksByCategory, getHooksConfigPath, getHooksForEvent, getIssueReferenceText, getLocalBranches, getRegistryPath, getRemoteBranches, getRepositoryRoot, getTools, getValidEventTypes, getValidModes, getValidOnFailureBehaviors, getWorktreeForBranch, hasDifferences, hasHooksForEvent, hasUncommittedChanges, isGitRepository, isTransientError, listAgents, listWorktrees, loadEventHooksConfig, loadHooksConfig, loadProjectConventions, loadRegistry, normalizeVSCodeSettings, parseBranchLink, parseGitHubUrl, parseIssueUrl, parseRateLimitDelay, parseSessionLine, pullLatest, queries, registerAgent, removeBranchLinkFromBody, removeEventHook, removeHook, removeWorktree, removeWorktreeWorkflow, resolveConflicts, sanitizeForBranchName, saveEventHooksConfig, saveHooksConfig, saveRegistry, setBranchLinkInBody, shellEscape, shouldAbort, skip, startIssueWorkflow, substituteTemplateVariables, toVSCodeSettings, unregisterAgent, updateAgent, updateEventHook, updateHook, useCli, useCustom, useVSCode, validateNumericInput, validateSafeString, validateUrl, withRetry, worktreeExists, wrapWithRetry };
3313
+ export { type ActivityEvent, type AgentInstance, type AgentRegistry, type AgentSessionStatus, type AgentStatus, type AgentSummary, type ApiKeyProvider, type AssigneeInfo, type AuthError, type BaseEventPayload, type BlockingIssue, type BlockingRelationships, type BranchDashboardData, BranchLinker, CLI_TO_VSCODE_MAP, ClaudeClient, type ClaudeClientOptions, type ClaudeResult, type ClaudeTool, type Collaborator, type Commit, type ConflictChoices, type ConflictResolution, type ContentBlock, type CreateIssueOptions, type CreateIssueResult, type CreatePROptions, type CreatePRResult, type CreateWorktreeOptions, type CreateWorktreeResult, DEFAULT_RETRY_CONFIG, DEFAULT_VALUES, type DashboardHook, type DashboardOptions, type DateFieldValue, type DiffStats, type EventHook, type EventHookSettings, type EventHooksConfig, type EventPayload, type EventType, type ExpandIssueOptions, type ExpandedIssue, type FieldInfo, type FieldValue, type FieldValueConnection, type FileChange, type FormatStandupOptions, GHP_TOOLS, type GeneratePRDescriptionOptions, GitError, GitHubAPI, type GitHubAPIOptions, type GitOptions, type HookExecutionOptions, type HookExecutionResult, type HookExitCodes, type HookItem, type HookMode, type HookOutcome, type HookResponse, type HookResult, type HooksConfig, type IssueActivity, type IssueCreatedPayload, type IssueDetails, type IssueReference, type IssueRelationships, type IssueStartedPayload, type IterationFieldValue, type LabelInfo, type Message, type NumberFieldValue, type OnFailureBehavior, type PRInfo, type PermissionPrompt, type PlanEpicOptions, type PlanEpicResult, type PrCreatedPayload, type PrCreatingPayload, type PrMergedPayload, type PrePrPayload, type Project, type ProjectConfig, type ProjectConventions, type ProjectItem, type ProjectItemContent, type ProjectItemsQueryResponse, type ProjectV2, type ProjectV2Field, type ProjectV2Item, type ProjectV2View, type ProjectWithViews, type ProjectsQueryResponse, type RegisterAgentOptions, type RelatedIssue, type RemoveWorktreeOptions, type RemoveWorktreeResult, type RepoInfo, type ResolvedClaudeConfig, type ResolvedSettings, type RetryConfig, SETTING_DISPLAY_NAMES, SYNCABLE_KEYS, type SessionEvent, SessionWatcher, type SettingConflict, type SettingsDiff, type SettingsSource, type SingleSelectFieldValue, type StartIssueOptions, type StartIssueResult, type StatusField, type StreamCallbacks, type StreamErrorEvent, type StreamEvent, type StreamEventBase, type StreamMessageCompleteEvent, type StreamOptions, type StreamTextEvent, type StreamToolInputDeltaEvent, type StreamToolUseCompleteEvent, type StreamToolUseStartEvent, type SyncableSettingKey, type SyncableSettings, TOOL_NAMES, type TextFieldValue, type TokenProvider, type TokenUsage, type ToolContext, type ToolHandler, type ToolHandlers, type UpdateAgentOptions, VSCODE_TO_CLI_MAP, type IssueInfo as WorkflowIssueInfo, type WorkflowResult, type WorktreeInfo as WorkflowWorktreeInfo, type WorktreeCreatedPayload, type WorktreeInfo$1 as WorktreeInfo, type WorktreeRemovedPayload, addEventHook, addHook, branchExists, buildConventionsContext, buildIssueUrl, buildOrgProjectUrl, buildProjectUrl, buildPullRequestUrl, buildRepoUrl, calculateBackoffDelay, checkTmuxForPermission, checkoutBranch, index as claudePrompts, cleanupStaleAgents, computeSettingsDiff, createBranch, createIssueWorkflow, createPRWorkflow, createSessionWatcher, createWorktree, createWorktreeWorkflow, detectRepository, disableEventHook, disableHook, enableEventHook, enableHook, executeAllHooks, executeEventHook, executeHook, executeHooksForEvent, extractIssueNumberFromBranch, fetchOrigin, findSessionFile, formatAction, formatConflict, formatStandupText, gatherDashboardData, generateBranchName, generateWorktreePath, getAgent, getAgentByIssue, getAgentSummaries, getAllBranches, getChangedFiles, getCommitHistory, getCommitsAhead, getCommitsBehind, getCurrentBranch$1 as getCurrentBranch, getCurrentBranch as getDashboardCurrentBranch, getDefaultBaseBranch, getDefaultBranch, getDiffStats, getDiffSummary, getEnabledEventHooks, getEnabledHooks, getEventHook, getEventHooks, getEventHooksConfigPath, getEventSettings, getFullDiff, getGitHubRepo, getHook, getHooks, getHooksByCategory, getHooksConfigPath, getHooksForEvent, getIssueReferenceText, getLocalBranches, getRegistryPath, getRemoteBranches, getRepositoryRoot, getTools, getValidEventTypes, getValidModes, getValidOnFailureBehaviors, getWorktreeForBranch, hasDifferences, hasHooksForEvent, hasUncommittedChanges, isGitRepository, isTransientError, listAgents, listWorktrees, loadEventHooksConfig, loadHooksConfig, loadProjectConventions, loadRegistry, normalizeVSCodeSettings, parseBranchLink, parseGitHubUrl, parseIssueUrl, parseRateLimitDelay, parseSessionLine, parseSince, pullLatest, queries, registerAgent, removeBranchLinkFromBody, removeEventHook, removeHook, removeWorktree, removeWorktreeWorkflow, resolveConflicts, sanitizeForBranchName, saveEventHooksConfig, saveHooksConfig, saveRegistry, setBranchLinkInBody, shellEscape, shouldAbort, skip, startIssueWorkflow, substituteTemplateVariables, toVSCodeSettings, unregisterAgent, updateAgent, updateEventHook, updateHook, useCli, useCustom, useVSCode, validateNumericInput, validateSafeString, validateUrl, withRetry, worktreeExists, wrapWithRetry };
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ __export(queries_exports, {
23
23
  ISSUE_FOR_UPDATE_QUERY: () => ISSUE_FOR_UPDATE_QUERY,
24
24
  ISSUE_NODE_ID_QUERY: () => ISSUE_NODE_ID_QUERY,
25
25
  ISSUE_RELATIONSHIPS_QUERY: () => ISSUE_RELATIONSHIPS_QUERY,
26
+ ISSUE_TIMELINE_QUERY: () => ISSUE_TIMELINE_QUERY,
26
27
  ISSUE_TYPES_QUERY: () => ISSUE_TYPES_QUERY,
27
28
  ISSUE_WITH_PROJECT_ITEMS_QUERY: () => ISSUE_WITH_PROJECT_ITEMS_QUERY,
28
29
  LABEL_EXISTS_QUERY: () => LABEL_EXISTS_QUERY,
@@ -113,6 +114,7 @@ var PROJECT_ITEMS_QUERY = `
113
114
  number
114
115
  url
115
116
  state
117
+ updatedAt
116
118
  issueType { name }
117
119
  assignees(first: 5) { nodes { login } }
118
120
  labels(first: 10) { nodes { name color } }
@@ -127,6 +129,7 @@ var PROJECT_ITEMS_QUERY = `
127
129
  number
128
130
  url
129
131
  state
132
+ updatedAt
130
133
  merged
131
134
  assignees(first: 5) { nodes { login } }
132
135
  labels(first: 10) { nodes { name color } }
@@ -490,6 +493,112 @@ var ISSUE_WITH_PROJECT_ITEMS_QUERY = `
490
493
  }
491
494
  }
492
495
  `;
496
+ var ISSUE_TIMELINE_QUERY = `
497
+ query($owner: String!, $name: String!, $number: Int!, $since: DateTime!) {
498
+ repository(owner: $owner, name: $name) {
499
+ issueOrPullRequest(number: $number) {
500
+ ... on Issue {
501
+ timelineItems(first: 100, since: $since) {
502
+ nodes {
503
+ __typename
504
+ ... on IssueComment {
505
+ author { login }
506
+ createdAt
507
+ body
508
+ }
509
+ ... on LabeledEvent {
510
+ actor { login }
511
+ createdAt
512
+ label { name }
513
+ }
514
+ ... on UnlabeledEvent {
515
+ actor { login }
516
+ createdAt
517
+ label { name }
518
+ }
519
+ ... on AssignedEvent {
520
+ actor { login }
521
+ createdAt
522
+ assignee { ... on User { login } }
523
+ }
524
+ ... on UnassignedEvent {
525
+ actor { login }
526
+ createdAt
527
+ assignee { ... on User { login } }
528
+ }
529
+ ... on ClosedEvent {
530
+ actor { login }
531
+ createdAt
532
+ }
533
+ ... on ReopenedEvent {
534
+ actor { login }
535
+ createdAt
536
+ }
537
+ ... on CrossReferencedEvent {
538
+ actor { login }
539
+ createdAt
540
+ source {
541
+ __typename
542
+ ... on PullRequest { number title url }
543
+ ... on Issue { number title }
544
+ }
545
+ }
546
+ }
547
+ }
548
+ }
549
+ ... on PullRequest {
550
+ timelineItems(first: 100, since: $since) {
551
+ nodes {
552
+ __typename
553
+ ... on IssueComment {
554
+ author { login }
555
+ createdAt
556
+ body
557
+ }
558
+ ... on LabeledEvent {
559
+ actor { login }
560
+ createdAt
561
+ label { name }
562
+ }
563
+ ... on UnlabeledEvent {
564
+ actor { login }
565
+ createdAt
566
+ label { name }
567
+ }
568
+ ... on AssignedEvent {
569
+ actor { login }
570
+ createdAt
571
+ assignee { ... on User { login } }
572
+ }
573
+ ... on UnassignedEvent {
574
+ actor { login }
575
+ createdAt
576
+ assignee { ... on User { login } }
577
+ }
578
+ ... on ClosedEvent {
579
+ actor { login }
580
+ createdAt
581
+ }
582
+ ... on ReopenedEvent {
583
+ actor { login }
584
+ createdAt
585
+ }
586
+ ... on CrossReferencedEvent {
587
+ actor { login }
588
+ createdAt
589
+ source {
590
+ __typename
591
+ ... on PullRequest { number title url }
592
+ ... on Issue { number title }
593
+ }
594
+ }
595
+ }
596
+ }
597
+ }
598
+ }
599
+ }
600
+ }
601
+ `;
493
602
  var ISSUE_RELATIONSHIPS_QUERY = `
494
603
  query($owner: String!, $name: String!, $number: Int!) {
495
604
  repository(owner: $owner, name: $name) {
@@ -895,7 +1004,8 @@ var GitHubAPI = class {
895
1004
  parent,
896
1005
  subIssues,
897
1006
  blockedBy,
898
- blocking
1007
+ blocking,
1008
+ updatedAt: content.updatedAt || null
899
1009
  };
900
1010
  });
901
1011
  }
@@ -1004,7 +1114,8 @@ var GitHubAPI = class {
1004
1114
  parent: issue.parent,
1005
1115
  subIssues: issue.subIssues.nodes,
1006
1116
  blockedBy: issue.blockedBy.nodes,
1007
- blocking: issue.blocking.nodes
1117
+ blocking: issue.blocking.nodes,
1118
+ updatedAt: null
1008
1119
  };
1009
1120
  } catch (error) {
1010
1121
  return null;
@@ -1566,6 +1677,158 @@ var GitHubAPI = class {
1566
1677
  return null;
1567
1678
  }
1568
1679
  }
1680
+ /**
1681
+ * Get recent activity across all project items since a given time.
1682
+ * Uses a two-pass approach for efficiency:
1683
+ * Pass 1: Fetch all project items, filter by updatedAt client-side
1684
+ * Pass 2: Fetch timeline events only for items that changed
1685
+ */
1686
+ async getRecentActivity(repo, since, options) {
1687
+ if (!this.graphqlWithAuth) throw new Error("Not authenticated");
1688
+ const projects = await this.getProjects(repo);
1689
+ if (projects.length === 0) return [];
1690
+ const allItems = [];
1691
+ for (const project of projects) {
1692
+ const items = await this.getProjectItems(project.id, project.title);
1693
+ allItems.push(...items);
1694
+ }
1695
+ const sinceMs = since.getTime();
1696
+ const sinceISO = since.toISOString();
1697
+ let recentItems = allItems.filter(
1698
+ (item) => item.updatedAt && new Date(item.updatedAt).getTime() >= sinceMs
1699
+ );
1700
+ if (options?.mine && this.username) {
1701
+ recentItems = recentItems.filter(
1702
+ (item) => item.assignees.includes(this.username)
1703
+ );
1704
+ }
1705
+ const seen = /* @__PURE__ */ new Set();
1706
+ recentItems = recentItems.filter((item) => {
1707
+ const key = `${item.repository || ""}#${item.number}`;
1708
+ if (!item.number || seen.has(key)) return false;
1709
+ seen.add(key);
1710
+ return true;
1711
+ });
1712
+ const activities = [];
1713
+ const BATCH_SIZE = 5;
1714
+ for (let i = 0; i < recentItems.length; i += BATCH_SIZE) {
1715
+ const batch = recentItems.slice(i, i + BATCH_SIZE);
1716
+ const results = await Promise.allSettled(
1717
+ batch.map(async (item) => {
1718
+ if (!item.number || !item.repository) return null;
1719
+ const [owner, name] = item.repository.split("/");
1720
+ if (!owner || !name) return null;
1721
+ const events = await this.fetchTimelineEvents(
1722
+ owner,
1723
+ name,
1724
+ item.number,
1725
+ sinceISO
1726
+ );
1727
+ if (events.length === 0) return null;
1728
+ return {
1729
+ issue: {
1730
+ number: item.number,
1731
+ title: item.title,
1732
+ url: item.url || ""
1733
+ },
1734
+ status: item.status,
1735
+ assignees: item.assignees,
1736
+ changes: events
1737
+ };
1738
+ })
1739
+ );
1740
+ for (const result of results) {
1741
+ if (result.status === "fulfilled" && result.value) {
1742
+ activities.push(result.value);
1743
+ }
1744
+ }
1745
+ }
1746
+ activities.sort((a, b) => {
1747
+ const aLatest = a.changes[a.changes.length - 1]?.timestamp || "";
1748
+ const bLatest = b.changes[b.changes.length - 1]?.timestamp || "";
1749
+ return bLatest.localeCompare(aLatest);
1750
+ });
1751
+ return activities;
1752
+ }
1753
+ /**
1754
+ * Fetch and normalize timeline events for a single issue/PR
1755
+ */
1756
+ async fetchTimelineEvents(owner, name, number, since) {
1757
+ if (!this.graphqlWithAuth) throw new Error("Not authenticated");
1758
+ const response = await this.graphqlWithRetry(
1759
+ ISSUE_TIMELINE_QUERY,
1760
+ { owner, name, number, since }
1761
+ );
1762
+ const item = response.repository.issueOrPullRequest;
1763
+ if (!item) return [];
1764
+ const events = [];
1765
+ for (const node of item.timelineItems.nodes) {
1766
+ const actor = node.actor?.login || node.author?.login || "unknown";
1767
+ const timestamp = node.createdAt || "";
1768
+ switch (node.__typename) {
1769
+ case "IssueComment":
1770
+ events.push({
1771
+ type: "comment",
1772
+ actor,
1773
+ timestamp,
1774
+ details: node.body ? node.body.substring(0, 80) + (node.body.length > 80 ? "..." : "") : void 0
1775
+ });
1776
+ break;
1777
+ case "LabeledEvent":
1778
+ events.push({
1779
+ type: "labeled",
1780
+ actor,
1781
+ timestamp,
1782
+ details: node.label?.name
1783
+ });
1784
+ break;
1785
+ case "UnlabeledEvent":
1786
+ events.push({
1787
+ type: "unlabeled",
1788
+ actor,
1789
+ timestamp,
1790
+ details: node.label?.name
1791
+ });
1792
+ break;
1793
+ case "AssignedEvent":
1794
+ events.push({
1795
+ type: "assigned",
1796
+ actor,
1797
+ timestamp,
1798
+ details: node.assignee?.login
1799
+ });
1800
+ break;
1801
+ case "UnassignedEvent":
1802
+ events.push({
1803
+ type: "unassigned",
1804
+ actor,
1805
+ timestamp,
1806
+ details: node.assignee?.login
1807
+ });
1808
+ break;
1809
+ case "ClosedEvent":
1810
+ events.push({ type: "closed", actor, timestamp });
1811
+ break;
1812
+ case "ReopenedEvent":
1813
+ events.push({ type: "reopened", actor, timestamp });
1814
+ break;
1815
+ case "CrossReferencedEvent": {
1816
+ const source = node.source;
1817
+ if (source) {
1818
+ const ref = source.__typename === "PullRequest" ? `PR #${source.number}: ${source.title}` : `#${source.number}: ${source.title}`;
1819
+ events.push({
1820
+ type: "referenced",
1821
+ actor,
1822
+ timestamp,
1823
+ details: ref
1824
+ });
1825
+ }
1826
+ break;
1827
+ }
1828
+ }
1829
+ }
1830
+ return events;
1831
+ }
1569
1832
  };
1570
1833
 
1571
1834
  // src/branch-linker.ts
@@ -3637,6 +3900,106 @@ function validateUrl(url) {
3637
3900
  return url;
3638
3901
  }
3639
3902
 
3903
+ // src/standup.ts
3904
+ function formatStandupText(activities, options) {
3905
+ const { since } = options;
3906
+ const lines = [];
3907
+ const sinceStr = formatRelativeDate(since);
3908
+ const issueCount = activities.length;
3909
+ lines.push(`Since ${sinceStr} \u2014 ${issueCount} issue${issueCount !== 1 ? "s" : ""} changed`);
3910
+ lines.push("");
3911
+ if (activities.length === 0) {
3912
+ lines.push("No activity found in this time window.");
3913
+ return lines.join("\n");
3914
+ }
3915
+ for (const activity of activities) {
3916
+ const statusTag = activity.status ? ` [${activity.status}]` : "";
3917
+ lines.push(`#${activity.issue.number} ${activity.issue.title}${statusTag}`);
3918
+ for (const event of activity.changes) {
3919
+ lines.push(` ${formatEventLine(event)}`);
3920
+ }
3921
+ lines.push("");
3922
+ }
3923
+ return lines.join("\n").trimEnd();
3924
+ }
3925
+ function parseSince(input) {
3926
+ const isoDate = new Date(input);
3927
+ if (!isNaN(isoDate.getTime()) && input.includes("-")) {
3928
+ return isoDate;
3929
+ }
3930
+ const match = input.match(/^(\d+)\s*(h|d|w)$/i);
3931
+ if (!match) {
3932
+ throw new Error(`Invalid duration format: "${input}". Use formats like "24h", "2d", "1w", or an ISO date.`);
3933
+ }
3934
+ const amount = parseInt(match[1], 10);
3935
+ const unit = match[2].toLowerCase();
3936
+ const now = /* @__PURE__ */ new Date();
3937
+ switch (unit) {
3938
+ case "h":
3939
+ return new Date(now.getTime() - amount * 60 * 60 * 1e3);
3940
+ case "d":
3941
+ return new Date(now.getTime() - amount * 24 * 60 * 60 * 1e3);
3942
+ case "w":
3943
+ return new Date(now.getTime() - amount * 7 * 24 * 60 * 60 * 1e3);
3944
+ default:
3945
+ throw new Error(`Unknown duration unit: "${unit}"`);
3946
+ }
3947
+ }
3948
+ function formatEventLine(event) {
3949
+ const arrow = "\u2197";
3950
+ const timestamp = formatShortTimestamp(event.timestamp);
3951
+ const actor = event.actor;
3952
+ switch (event.type) {
3953
+ case "comment":
3954
+ return `${arrow} Comment by ${actor} (${timestamp})${event.details ? ": " + event.details : ""}`;
3955
+ case "labeled":
3956
+ return `${arrow} Labeled "${event.details}" by ${actor} (${timestamp})`;
3957
+ case "unlabeled":
3958
+ return `${arrow} Unlabeled "${event.details}" by ${actor} (${timestamp})`;
3959
+ case "assigned":
3960
+ return `${arrow} Assigned to ${event.details || actor} (${timestamp})`;
3961
+ case "unassigned":
3962
+ return `${arrow} Unassigned ${event.details || ""} by ${actor} (${timestamp})`;
3963
+ case "closed":
3964
+ return `${arrow} Closed by ${actor} (${timestamp})`;
3965
+ case "reopened":
3966
+ return `${arrow} Reopened by ${actor} (${timestamp})`;
3967
+ case "referenced":
3968
+ return `${arrow} ${event.details} linked by ${actor} (${timestamp})`;
3969
+ default:
3970
+ return `${arrow} ${event.type} by ${actor} (${timestamp})`;
3971
+ }
3972
+ }
3973
+ function formatRelativeDate(date) {
3974
+ const now = /* @__PURE__ */ new Date();
3975
+ const diffMs = now.getTime() - date.getTime();
3976
+ const diffHours = Math.round(diffMs / (1e3 * 60 * 60));
3977
+ const dateStr = date.toLocaleDateString("en-US", {
3978
+ month: "short",
3979
+ day: "numeric",
3980
+ hour: "2-digit",
3981
+ minute: "2-digit"
3982
+ });
3983
+ if (diffHours < 24) {
3984
+ return `${diffHours}h ago (${dateStr})`;
3985
+ } else if (diffHours < 48) {
3986
+ return `yesterday (${dateStr})`;
3987
+ } else {
3988
+ const diffDays = Math.round(diffHours / 24);
3989
+ return `${diffDays} days ago (${dateStr})`;
3990
+ }
3991
+ }
3992
+ function formatShortTimestamp(isoTimestamp) {
3993
+ if (!isoTimestamp) return "";
3994
+ const date = new Date(isoTimestamp);
3995
+ return date.toLocaleDateString("en-US", {
3996
+ month: "short",
3997
+ day: "numeric",
3998
+ hour: "2-digit",
3999
+ minute: "2-digit"
4000
+ });
4001
+ }
4002
+
3640
4003
  // src/dashboard/index.ts
3641
4004
  import { exec as exec3 } from "child_process";
3642
4005
  import { promisify as promisify3 } from "util";
@@ -5093,6 +5456,7 @@ export {
5093
5456
  findSessionFile,
5094
5457
  formatAction,
5095
5458
  formatConflict,
5459
+ formatStandupText,
5096
5460
  gatherDashboardData,
5097
5461
  generateBranchName,
5098
5462
  generateWorktreePath,
@@ -5150,6 +5514,7 @@ export {
5150
5514
  parseIssueUrl,
5151
5515
  parseRateLimitDelay,
5152
5516
  parseSessionLine,
5517
+ parseSince,
5153
5518
  pullLatest,
5154
5519
  queries_exports as queries,
5155
5520
  registerAgent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bretwardjames/ghp-core",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Shared core library for GitHub Projects tools",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",