Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/full-ends-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@plotday/tool-outlook-calendar": minor
"@plotday/tool-google-calendar": minor
"@plotday/tool-github-issues": minor
"@plotday/tool-google-drive": minor
"@plotday/tool-github": minor
"@plotday/tool-linear": minor
"@plotday/tool-asana": minor
"@plotday/tool-jira": minor
"@plotday/twister": minor
---

Added: Activity.links, better for activity-scoped links such as the link to the original item
4 changes: 2 additions & 2 deletions tools/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export { default, ToolName } from "./tool-name";
```typescript
import {
ActivityType,
ActivityLinkType,
LinkType,
type NewActivity,
type NewActivityWithNotes,
type NewNote,
Expand Down Expand Up @@ -346,7 +346,7 @@ export class MyTool extends Tool<MyTool> implements ProjectTool {
key: "description", // Enables note upsert
content: item.description || null,
links: item.url ? [{
type: ActivityLinkType.external,
type: LinkType.external,
title: "Open in Service",
url: item.url,
}] : null,
Expand Down
15 changes: 8 additions & 7 deletions tools/asana/src/asana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import * as asana from "asana";
import {
type Activity,
type ActivityFilter,
type ActivityLink,
ActivityLinkType,
type Link,
LinkType,
ActivityMeta,
ActivityType,
type NewActivity,
Expand Down Expand Up @@ -424,20 +424,20 @@ export class Asana extends Tool<Asana> implements ProjectTool {
// Construct Asana task URL for link
const taskUrl = `https://app.asana.com/0/${projectId}/${task.gid}`;

// Create initial note with description and link to Asana task
const links: ActivityLink[] = [];
links.push({
type: ActivityLinkType.external,
// Build activity-level links
const activityLinks: Link[] = [];
activityLinks.push({
type: LinkType.external,
title: `Open in Asana`,
url: taskUrl,
});

// Create initial note with description (links moved to activity level)
notes.push({
activity: { source: activitySource },
key: "description",
content: description,
created: task.created_at ? new Date(task.created_at) : undefined,
links: links.length > 0 ? links : null,
});

return {
Expand All @@ -451,6 +451,7 @@ export class Asana extends Tool<Asana> implements ProjectTool {
syncProvider: "asana",
syncableId: projectId,
},
links: activityLinks.length > 0 ? activityLinks : undefined,
author: authorContact,
assignee: assigneeContact ?? null, // Explicitly set to null for unassigned tasks
done:
Expand Down
20 changes: 10 additions & 10 deletions tools/github-issues/src/github-issues.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Octokit } from "@octokit/rest";

import {
type ActivityLink,
ActivityLinkType,
type Link,
LinkType,
type ActivityMeta,
ActivityType,
type NewActivity,
Expand Down Expand Up @@ -453,24 +453,23 @@ export class GitHubIssues extends Tool<GitHubIssues> implements ProjectTool {
const description = issue.body || "";
const hasDescription = description.trim().length > 0;

// Build notes array (inline notes don't require the `activity` field)
const notes: any[] = [];

// Description note with link to GitHub issue
const links: ActivityLink[] = [];
// Build activity-level links
const activityLinks: Link[] = [];
if (issue.html_url) {
links.push({
type: ActivityLinkType.external,
activityLinks.push({
type: LinkType.external,
title: "Open in GitHub",
url: issue.html_url,
});
}

// Build notes array (inline notes don't require the `activity` field)
const notes: any[] = [];

notes.push({
key: "description",
content: hasDescription ? description : null,
created: issue.created_at,
links: links.length > 0 ? links : null,
author: authorContact,
});

Expand Down Expand Up @@ -534,6 +533,7 @@ export class GitHubIssues extends Tool<GitHubIssues> implements ProjectTool {
githubRepoFullName: repoFullName,
projectId: repoId,
},
links: activityLinks.length > 0 ? activityLinks : undefined,
notes,
preview: hasDescription ? description : null,
...(initialSync ? { unread: false } : {}),
Expand Down
16 changes: 8 additions & 8 deletions tools/github/src/github.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
type Activity,
type ActivityLink,
ActivityLinkType,
type Link,
LinkType,
type ActivityMeta,
ActivityType,
type NewActivity,
Expand Down Expand Up @@ -784,23 +784,22 @@ export class GitHub extends Tool<GitHub> implements SourceControlTool {
? this.userToContact(pr.assignee)
: null;

const notes: any[] = [];

// Description note with link to GitHub PR
const links: ActivityLink[] = [
// Build activity-level links
const activityLinks: Link[] = [
{
type: ActivityLinkType.external,
type: LinkType.external,
title: `Open in GitHub`,
url: pr.html_url,
},
];

const notes: any[] = [];

const hasDescription = pr.body && pr.body.trim().length > 0;
notes.push({
key: "description",
content: hasDescription ? pr.body : null,
created: new Date(pr.created_at),
links,
author: authorContact,
});

Expand Down Expand Up @@ -873,6 +872,7 @@ export class GitHub extends Tool<GitHub> implements SourceControlTool {
prNumber: pr.number,
prNodeId: pr.id,
},
links: activityLinks,
notes,
preview: hasDescription ? pr.body : null,
...(initialSync ? { unread: false } : {}),
Expand Down
20 changes: 10 additions & 10 deletions tools/google-calendar/src/google-calendar.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import GoogleContacts from "@plotday/tool-google-contacts";
import {
type Activity,
ActivityLinkType,
type ActivityLink,
LinkType,
type Link,
type ActivityOccurrence,
ActivityType,
type ActorId,
Expand Down Expand Up @@ -832,7 +832,7 @@ export class GoogleCalendar
}

// Build links array for videoconferencing and calendar links
const links: ActivityLink[] = [];
const links: Link[] = [];
const seenUrls = new Set<string>();

// Extract all conferencing links (Zoom, Teams, Webex, etc.)
Expand All @@ -841,7 +841,7 @@ export class GoogleCalendar
if (!seenUrls.has(link.url)) {
seenUrls.add(link.url);
links.push({
type: ActivityLinkType.conferencing,
type: LinkType.conferencing,
url: link.url,
provider: link.provider,
});
Expand All @@ -852,7 +852,7 @@ export class GoogleCalendar
if (event.hangoutLink && !seenUrls.has(event.hangoutLink)) {
seenUrls.add(event.hangoutLink);
links.push({
type: ActivityLinkType.conferencing,
type: LinkType.conferencing,
url: event.hangoutLink,
provider: ConferencingProvider.googleMeet,
});
Expand All @@ -861,7 +861,7 @@ export class GoogleCalendar
// Add calendar link
if (event.htmlLink) {
links.push({
type: ActivityLinkType.external,
type: LinkType.external,
title: "View in Calendar",
url: event.htmlLink,
});
Expand All @@ -882,14 +882,13 @@ export class GoogleCalendar
// Canonical source for this event (required for upsert)
const canonicalUrl = `google-calendar:${event.id}`;

// Create note with description and/or links
// Create note with description (links moved to activity level)
const notes: NewNote[] = [];
if (hasDescription || hasLinks) {
if (hasDescription) {
notes.push({
activity: { source: canonicalUrl },
key: "description",
content: hasDescription ? description : null,
links: hasLinks ? links : null,
content: description,
contentType:
description && containsHtml(description) ? "html" : "text",
created: event.created ? new Date(event.created) : new Date(),
Expand All @@ -909,6 +908,7 @@ export class GoogleCalendar
recurrenceExdates: activityData.recurrenceExdates || null,
meta: activityData.meta ?? null,
tags: tags || undefined,
links: hasLinks ? links : undefined,
notes,
preview: hasDescription ? description : null,
...(initialSync ? { unread: false } : {}), // false for initial sync, omit for incremental updates
Expand Down
14 changes: 5 additions & 9 deletions tools/google-drive/src/google-drive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import GoogleContacts from "@plotday/tool-google-contacts";
import {
type ActivityFilter,
ActivityKind,
type ActivityLink,
ActivityLinkType,
type Link,
LinkType,
ActivityType,
type NewActivityWithNotes,
type NewContact,
Expand Down Expand Up @@ -778,26 +778,22 @@ export class GoogleDrive extends Tool<GoogleDrive> implements DocumentTool {
}

// Build external link
const links: ActivityLink[] = [];
const links: Link[] = [];
if (file.webViewLink) {
links.push({
type: ActivityLinkType.external,
type: LinkType.external,
title: "View in Drive",
url: file.webViewLink,
});
}

// Add links to the summary note if present
if (links.length > 0 && notes.length > 0) {
notes[0].links = links;
}

const activity: NewActivityWithNotes = {
source: canonicalSource,
type: ActivityType.Note,
kind: ActivityKind.document,
title: file.name,
author,
links: links.length > 0 ? links : null,
meta: {
fileId: file.id,
folderId,
Expand Down
15 changes: 8 additions & 7 deletions tools/jira/src/jira.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Version3Client } from "jira.js";

import {
type Activity,
type ActivityLink,
ActivityLinkType,
type Link,
LinkType,
ActivityType,
type NewActivity,
type NewActivityWithNotes,
Expand Down Expand Up @@ -430,21 +430,21 @@ export class Jira extends Tool<Jira> implements ProjectTool {
? `jira:${cloudId}:issue:${issue.id}`
: undefined;

// Create initial note with description and link to Jira issue
const links: ActivityLink[] = [];
// Build activity-level links
const activityLinks: Link[] = [];
if (issueUrl) {
links.push({
type: ActivityLinkType.external,
activityLinks.push({
type: LinkType.external,
title: `Open in Jira`,
url: issueUrl,
});
}

// Create initial note with description (links moved to activity level)
notes.push({
key: "description",
content: description,
created: fields.created ? new Date(fields.created) : undefined,
links: links.length > 0 ? links : null,
author: authorContact,
});

Expand Down Expand Up @@ -486,6 +486,7 @@ export class Jira extends Tool<Jira> implements ProjectTool {
author: authorContact,
assignee: assigneeContact ?? null, // Explicitly set to null for unassigned issues
done: fields.resolutiondate ? new Date(fields.resolutiondate) : null,
links: activityLinks.length > 0 ? activityLinks : undefined,
notes,
preview: description || null,
};
Expand Down
20 changes: 10 additions & 10 deletions tools/linear/src/linear.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import type {
import { LinearWebhookClient } from "@linear/sdk/webhooks";

import {
type ActivityLink,
ActivityLinkType,
type Link,
LinkType,
ActivityMeta,
ActivityType,
type NewActivity,
Expand Down Expand Up @@ -420,24 +420,23 @@ export class Linear extends Tool<Linear> implements ProjectTool {
const description = issue.description || "";
const hasDescription = description.trim().length > 0;

// Build notes array: description note with link + comment notes
const notes: any[] = [];

// Create description note with link to Linear issue
const links: ActivityLink[] = [];
// Build activity-level links
const activityLinks: Link[] = [];
if (issue.url) {
links.push({
type: ActivityLinkType.external,
activityLinks.push({
type: LinkType.external,
title: `Open in Linear`,
url: issue.url,
});
}

// Build notes array: description note + comment notes
const notes: any[] = [];

notes.push({
key: "description",
content: hasDescription ? description : null,
created: issue.createdAt,
links: links.length > 0 ? links : null,
author: authorContact,
});

Expand Down Expand Up @@ -483,6 +482,7 @@ export class Linear extends Tool<Linear> implements ProjectTool {
linearId: issue.id,
projectId,
},
links: activityLinks.length > 0 ? activityLinks : undefined,
notes,
preview: hasDescription ? description : null,
...(initialSync ? { unread: false } : {}), // false for initial sync, omit for incremental updates
Expand Down
Loading