Compare commits

...

2 Commits

Author SHA1 Message Date
thomas 03efbd500e fix nvim 2026-03-19 16:37:29 +00:00
thomas 227c1638f6 linear skill 2026-03-19 16:37:27 +00:00
16 changed files with 833 additions and 3 deletions
+1 -2
View File
@@ -89,8 +89,7 @@ vim.o.confirm = true
-- vim.o.winborder = "rounded" -- vim.o.winborder = "rounded"
-- Clipboard: prefer system clipboard, and over SSH use OSC52 so yanks can reach local clipboard -- Clipboard: keep default y/p behavior; over SSH, route + register through OSC52
vim.opt.clipboard = "unnamedplus"
if vim.env.SSH_TTY then if vim.env.SSH_TTY then
vim.g.clipboard = "osc52" vim.g.clipboard = "osc52"
end end
+1 -1
View File
@@ -10,4 +10,4 @@
"input.required": true, "input.required": true,
"resource.limit": true "resource.limit": true
} }
} }
@@ -1,3 +1,8 @@
---
name: attio-frontend-rules
description: Styling conventions and component guidelines for the Attio frontend codebase. Covers styled-components patterns, transient props, data attributes, spacing, color tokens, and design system usage. Use when modifying frontend UI code in the Attio monorepo.
---
# Attio Frontend Rules # Attio Frontend Rules
Guidelines and conventions for working on the Attio frontend codebase. Use whenever modifying the frontend. Guidelines and conventions for working on the Attio frontend codebase. Use whenever modifying the frontend.
@@ -52,6 +57,26 @@ export function Stack({..., className}: {..., className: string | undefined}) {
If the same re-styling is applied multiple times, it should become its own reusable component (or component variant). If the same re-styling is applied multiple times, it should become its own reusable component (or component variant).
### Layout.Stack defaults
`Layout.Stack` defaults `align` to `"center"` (i.e. `align-items: center`). **Always explicitly set `align="flex-start"`** when you need left/top alignment — don't assume it will be the default.
```tsx
// Good — explicit alignment
<Layout.Stack direction="column" align="flex-start">
<Typography.Body.Standard.Component>Title</Typography.Body.Standard.Component>
<Typography.Caption.Standard.Component>Description</Typography.Caption.Standard.Component>
</Layout.Stack>
// Bad — text will be centered, not left-aligned
<Layout.Stack direction="column">
<Typography.Body.Standard.Component>Title</Typography.Body.Standard.Component>
<Typography.Caption.Standard.Component>Description</Typography.Caption.Standard.Component>
</Layout.Stack>
```
Other useful `Layout.Stack` props: `direction`, `justify`, `gap`, `flex`, `shrink`, `minWidth`, `width`, `height`, and all spacing props (`p`, `px`, `py`, `pt`, `pb`, `pl`, `pr`, `m`, `mx`, `my`, etc.). **Always prefer these props over writing custom styled divs with `display: flex`.**
### Avoid layout assumptions ### Avoid layout assumptions
Components should not generally include external layout styles such as `width`, `z-index`, `margin` or `flex`. These properties should instead be set by the parent component using a `styled(MyComponent)` override. Components should not generally include external layout styles such as `width`, `z-index`, `margin` or `flex`. These properties should instead be set by the parent component using a `styled(MyComponent)` override.
+105
View File
@@ -0,0 +1,105 @@
---
name: linear
description: Access Linear issue tracker - search, view, create, update issues, list teams/projects, and manage comments. Use when the user asks about Linear issues, tasks, tickets, or project management in Linear.
---
# Linear
Manage Linear issues, projects, and teams via the Linear SDK.
## Setup
Run once before first use:
```bash
cd {baseDir} && npm install
```
Requires a `LINEAR_API_KEY` environment variable. Generate one at: https://linear.app/settings/api (Personal API keys).
Set it in your shell profile or pi settings:
```bash
export LINEAR_API_KEY=lin_api_...
```
## Current User
```bash
node {baseDir}/linear-me.js # Show authenticated user
node {baseDir}/linear-me.js --issues # Show user + their active issues
```
## Search Issues
```bash
node {baseDir}/linear-search.js "query" # Text search
node {baseDir}/linear-search.js "query" -n 20 # More results
node {baseDir}/linear-search.js "query" --team ENG # Filter by team
node {baseDir}/linear-search.js "query" --state "In Progress" # Filter by state
```
## List Issues (with filters)
```bash
node {baseDir}/linear-issues.js # All recent issues
node {baseDir}/linear-issues.js --team ENG # By team
node {baseDir}/linear-issues.js --state "In Progress" # By state
node {baseDir}/linear-issues.js --assignee me # My issues
node {baseDir}/linear-issues.js --assignee "John" # By assignee name
node {baseDir}/linear-issues.js --label "Bug" # By label
node {baseDir}/linear-issues.js --project "Q1 Goals" # By project
node {baseDir}/linear-issues.js --team ENG --state Todo -n 50 # Combined filters
```
## View Issue Details
```bash
node {baseDir}/linear-issue.js ATT-1234 # Full issue details
node {baseDir}/linear-issue.js ATT-1234 --comments # Include comments
```
## Create Issue
```bash
node {baseDir}/linear-create.js --team ENG --title "Fix login bug"
node {baseDir}/linear-create.js --team ENG --title "New feature" --description "Details here" --state Todo --priority 2 --assignee me --label "Feature"
node {baseDir}/linear-create.js --team ENG --title "Sub-task" --parent ATT-100
```
Priority values: 0=None, 1=Urgent, 2=High, 3=Medium, 4=Low
## Update Issue
```bash
node {baseDir}/linear-update.js ATT-1234 --state "In Progress"
node {baseDir}/linear-update.js ATT-1234 --assignee me --priority 2
node {baseDir}/linear-update.js ATT-1234 --title "New title" --description "Updated desc"
```
## Add Comment
```bash
node {baseDir}/linear-comment.js ATT-1234 "This is done in PR #567"
```
## List Teams
```bash
node {baseDir}/linear-teams.js
```
## List Projects
```bash
node {baseDir}/linear-projects.js # All projects
node {baseDir}/linear-projects.js --team ENG # By team
```
## Tips
- Use `--assignee me` to filter by the authenticated user
- Issue identifiers follow the pattern `TEAM-NUMBER` (e.g. `ATT-1234`, `ENG-567`)
- Descriptions support markdown formatting
- State names are case-insensitive (e.g. "todo", "Todo", "TODO" all work)
- When creating issues, the team key is required; use `linear-teams.js` to find available teams
+23
View File
@@ -0,0 +1,23 @@
import { LinearClient } from "@linear/sdk";
export function getClient() {
const apiKey = process.env.LINEAR_API_KEY;
if (!apiKey) {
console.error("Error: LINEAR_API_KEY environment variable is required.");
console.error(
"Generate one at: https://linear.app/settings/api (Personal API keys)"
);
process.exit(1);
}
return new LinearClient({ apiKey });
}
export function formatDate(date) {
if (!date) return "";
return new Date(date).toISOString().split("T")[0];
}
export function truncate(str, len = 120) {
if (!str) return "";
return str.length > len ? str.slice(0, len) + "…" : str;
}
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env node
// Add a comment to a Linear issue
// Usage: linear-comment.js <identifier> <body>
import { getClient } from "./lib.js";
const args = process.argv.slice(2);
const identifier = args[0];
const body = args.slice(1).join(" ");
if (!identifier || !body) {
console.log("Usage: linear-comment.js <identifier> <body>");
console.log("\nExamples:");
console.log(' linear-comment.js ATT-1234 "This is fixed in the latest PR"');
process.exit(1);
}
const client = getClient();
const results = await client.searchIssues(identifier, { first: 1 });
const issue = results.nodes[0];
if (!issue) {
console.error(`Issue '${identifier}' not found.`);
process.exit(1);
}
await client.createComment({ issueId: issue.id, body });
console.log(`Comment added to ${issue.identifier}.`);
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env node
// Create a new Linear issue
// Usage: linear-create.js --team <key> --title <title> [--description <desc>] [--state <name>] [--priority <0-4>] [--assignee <name|me>] [--label <name>] [--parent <identifier>]
import { getClient } from "./lib.js";
const args = process.argv.slice(2);
function extractArg(flag) {
const idx = args.indexOf(flag);
if (idx !== -1 && args[idx + 1]) {
const val = args[idx + 1];
args.splice(idx, 2);
return val;
}
return null;
}
const teamKey = extractArg("--team");
const title = extractArg("--title");
const description = extractArg("--description");
const stateName = extractArg("--state");
const priority = extractArg("--priority");
const assigneeName = extractArg("--assignee");
const labelName = extractArg("--label");
const parentId = extractArg("--parent");
if (!teamKey || !title) {
console.log("Usage: linear-create.js --team <key> --title <title> [options]");
console.log("\nRequired:");
console.log(" --team <key> Team key (e.g. ENG)");
console.log(' --title <title> Issue title');
console.log("\nOptional:");
console.log(" --description <text> Issue description (markdown)");
console.log(" --state <name> Initial state (e.g. 'Todo')");
console.log(" --priority <0-4> Priority: 0=None, 1=Urgent, 2=High, 3=Medium, 4=Low");
console.log(" --assignee <name|me> Assignee name or 'me'");
console.log(" --label <name> Label name");
console.log(" --parent <id> Parent issue identifier (e.g. ATT-100)");
process.exit(1);
}
const client = getClient();
// Resolve team
const teams = await client.teams({ filter: { key: { eq: teamKey.toUpperCase() } } });
const team = teams.nodes[0];
if (!team) {
console.error(`Team '${teamKey}' not found.`);
process.exit(1);
}
const input = {
teamId: team.id,
title,
};
if (description) input.description = description;
if (priority) input.priority = parseInt(priority, 10);
// Resolve state
if (stateName) {
const states = await team.states();
const state = states.nodes.find(
(s) => s.name.toLowerCase() === stateName.toLowerCase()
);
if (state) input.stateId = state.id;
else console.warn(`Warning: State '${stateName}' not found, using default.`);
}
// Resolve assignee
if (assigneeName) {
if (assigneeName.toLowerCase() === "me") {
const me = await client.viewer;
input.assigneeId = me.id;
} else {
const users = await client.users({ filter: { name: { containsIgnoreCase: assigneeName } } });
if (users.nodes[0]) input.assigneeId = users.nodes[0].id;
else console.warn(`Warning: User '${assigneeName}' not found.`);
}
}
// Resolve label
if (labelName) {
const labels = await client.issueLabels({ filter: { name: { eqIgnoreCase: labelName } } });
if (labels.nodes[0]) input.labelIds = [labels.nodes[0].id];
else console.warn(`Warning: Label '${labelName}' not found.`);
}
// Resolve parent
if (parentId) {
const parentSearch = await client.searchIssues(parentId, { first: 1 });
if (parentSearch.nodes[0]) input.parentId = parentSearch.nodes[0].id;
else console.warn(`Warning: Parent '${parentId}' not found.`);
}
const result = await client.createIssue(input);
const issue = await result.issue;
console.log(`Created: ${issue.identifier} - ${issue.title}`);
console.log(`URL: ${issue.url}`);
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env node
// Get details for a specific Linear issue
// Usage: linear-issue.js <identifier> [--comments]
import { getClient, formatDate } from "./lib.js";
const args = process.argv.slice(2);
const showComments = args.includes("--comments");
const filtered = args.filter((a) => a !== "--comments");
const identifier = filtered[0];
if (!identifier) {
console.log("Usage: linear-issue.js <identifier> [--comments]");
console.log("\nExamples:");
console.log(" linear-issue.js ATT-1234");
console.log(" linear-issue.js ATT-1234 --comments");
process.exit(1);
}
const client = getClient();
// Parse team key and issue number from identifier (e.g. "SIP-1205")
const parts = identifier.match(/^([A-Za-z]+)-(\d+)$/);
if (!parts) {
console.error(`Invalid identifier format: ${identifier}. Expected format: TEAM-123`);
process.exit(1);
}
const teamKey = parts[1].toUpperCase();
const issueNumber = parseInt(parts[2], 10);
// Find the issue by team key + number
const issues = await client.issues({
filter: {
team: { key: { eq: teamKey } },
number: { eq: issueNumber },
},
first: 1,
});
const issue = issues.nodes[0];
if (!issue) {
console.error(`Issue ${identifier} not found.`);
process.exit(1);
}
const state = await issue.state;
const team = await issue.team;
const assignee = await issue.assignee;
const labels = await issue.labels();
const parent = await issue.parent;
const project = await issue.project;
const cycle = await issue.cycle;
console.log(`=== ${issue.identifier}: ${issue.title} ===`);
console.log(`URL: ${issue.url}`);
console.log(`State: ${state?.name || "Unknown"}`);
console.log(`Priority: ${issue.priorityLabel}`);
console.log(`Team: ${team?.key || "?"}`);
console.log(`Assignee: ${assignee?.name || "Unassigned"}`);
if (project) console.log(`Project: ${project.name}`);
if (cycle) console.log(`Cycle: ${cycle.name || cycle.number}`);
if (parent) console.log(`Parent: ${parent.identifier} - ${parent.title}`);
if (labels.nodes.length > 0) {
console.log(`Labels: ${labels.nodes.map((l) => l.name).join(", ")}`);
}
console.log(`Created: ${formatDate(issue.createdAt)}`);
console.log(`Updated: ${formatDate(issue.updatedAt)}`);
if (issue.dueDate) console.log(`Due: ${issue.dueDate}`);
console.log(`\nDescription:\n${issue.description || "(empty)"}`);
if (showComments) {
const comments = await issue.comments();
if (comments.nodes.length > 0) {
console.log(`\n--- Comments (${comments.nodes.length}) ---`);
for (const comment of comments.nodes) {
const author = await comment.user;
console.log(`\n[${formatDate(comment.createdAt)}] ${author?.name || "Unknown"}:`);
console.log(comment.body);
}
} else {
console.log("\nNo comments.");
}
}
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env node
// List Linear issues with filters
// Usage: linear-issues.js [--team <key>] [--state <name>] [--assignee <name|me>] [--label <name>] [--project <name>] [-n <num>]
import { getClient, formatDate, truncate } from "./lib.js";
const args = process.argv.slice(2);
function extractArg(flag) {
const idx = args.indexOf(flag);
if (idx !== -1 && args[idx + 1]) {
const val = args[idx + 1];
args.splice(idx, 2);
return val;
}
return null;
}
const numResults = parseInt(extractArg("-n") || "25", 10);
const teamKey = extractArg("--team");
const stateName = extractArg("--state");
const assigneeName = extractArg("--assignee");
const labelName = extractArg("--label");
const projectName = extractArg("--project");
if (args.includes("--help") || args.includes("-h")) {
console.log("Usage: linear-issues.js [options]");
console.log("\nOptions:");
console.log(" --team <key> Filter by team key (e.g. ENG)");
console.log(" --state <name> Filter by state (e.g. 'In Progress', 'Todo')");
console.log(" --assignee <name> Filter by assignee name or 'me'");
console.log(" --label <name> Filter by label name");
console.log(" --project <name> Filter by project name");
console.log(" -n <num> Number of results (default: 25)");
process.exit(0);
}
const client = getClient();
// Build filter
const filter = {};
if (teamKey) {
filter.team = { key: { eq: teamKey.toUpperCase() } };
}
if (stateName) {
filter.state = { name: { eqIgnoreCase: stateName } };
}
if (assigneeName) {
if (assigneeName.toLowerCase() === "me") {
const me = await client.viewer;
filter.assignee = { id: { eq: me.id } };
} else {
filter.assignee = { name: { containsIgnoreCase: assigneeName } };
}
}
if (labelName) {
filter.labels = { name: { eqIgnoreCase: labelName } };
}
if (projectName) {
filter.project = { name: { containsIgnoreCase: projectName } };
}
const issues = await client.issues({
filter,
first: numResults,
orderBy: "updatedAt",
});
if (issues.nodes.length === 0) {
console.log("No issues found matching filters.");
process.exit(0);
}
for (const issue of issues.nodes) {
const state = await issue.state;
const team = await issue.team;
const assignee = await issue.assignee;
console.log(
`${issue.identifier.padEnd(12)} ${(state?.name || "?").padEnd(14)} ${(issue.priorityLabel || "").padEnd(8)} ${(assignee?.name || "Unassigned").padEnd(20)} ${truncate(issue.title, 80)}`
);
}
console.log(`\n${issues.nodes.length} issue(s) shown.`);
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env node
// Show current authenticated user and their assigned issues
// Usage: linear-me.js [--issues]
import { getClient, truncate } from "./lib.js";
const showIssues = process.argv.includes("--issues");
const client = getClient();
const me = await client.viewer;
console.log(`User: ${me.name}`);
console.log(`Email: ${me.email}`);
console.log(`ID: ${me.id}`);
if (showIssues) {
const issues = await me.assignedIssues({
first: 25,
filter: {
state: { type: { nin: ["completed", "canceled"] } },
},
orderBy: "updatedAt",
});
console.log(`\n--- Active Assigned Issues (${issues.nodes.length}) ---`);
for (const issue of issues.nodes) {
const state = await issue.state;
console.log(
`${issue.identifier.padEnd(12)} ${(state?.name || "?").padEnd(14)} ${(issue.priorityLabel || "").padEnd(8)} ${truncate(issue.title, 80)}`
);
}
}
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env node
// List Linear projects
// Usage: linear-projects.js [--team <key>] [-n <num>]
import { getClient, formatDate } from "./lib.js";
const args = process.argv.slice(2);
function extractArg(flag) {
const idx = args.indexOf(flag);
if (idx !== -1 && args[idx + 1]) {
const val = args[idx + 1];
args.splice(idx, 2);
return val;
}
return null;
}
const numResults = parseInt(extractArg("-n") || "25", 10);
const teamKey = extractArg("--team");
const client = getClient();
const filter = {};
if (teamKey) {
filter.accessibleTeams = { key: { eq: teamKey.toUpperCase() } };
}
const projects = await client.projects({ filter, first: numResults });
if (projects.nodes.length === 0) {
console.log("No projects found.");
process.exit(0);
}
for (const project of projects.nodes) {
const lead = await project.lead;
console.log(`--- ${project.name} ---`);
console.log(`State: ${project.state} | Progress: ${Math.round(project.progress * 100)}%`);
if (lead) console.log(`Lead: ${lead.name}`);
if (project.targetDate) console.log(`Target: ${project.targetDate}`);
console.log(`URL: ${project.url}`);
console.log("");
}
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env node
// Search Linear issues by text query
// Usage: linear-search.js <query> [-n <num>] [--team <key>] [--state <name>]
import { getClient, formatDate, truncate } from "./lib.js";
const args = process.argv.slice(2);
let numResults = 10;
const nIdx = args.indexOf("-n");
if (nIdx !== -1 && args[nIdx + 1]) {
numResults = parseInt(args[nIdx + 1], 10);
args.splice(nIdx, 2);
}
let teamFilter = null;
const teamIdx = args.indexOf("--team");
if (teamIdx !== -1 && args[teamIdx + 1]) {
teamFilter = args[teamIdx + 1];
args.splice(teamIdx, 2);
}
let stateFilter = null;
const stateIdx = args.indexOf("--state");
if (stateIdx !== -1 && args[stateIdx + 1]) {
stateFilter = args[stateIdx + 1];
args.splice(stateIdx, 2);
}
const query = args.join(" ");
if (!query) {
console.log("Usage: linear-search.js <query> [-n <num>] [--team <key>] [--state <name>]");
console.log("\nOptions:");
console.log(" -n <num> Number of results (default: 10)");
console.log(" --team <key> Filter by team key (e.g. ENG)");
console.log(" --state <name> Filter by state name (e.g. 'In Progress')");
process.exit(1);
}
const client = getClient();
const results = await client.searchIssues(query, { first: numResults });
for (const issue of results.nodes) {
const state = await issue.state;
const team = await issue.team;
const assignee = await issue.assignee;
if (teamFilter && team?.key?.toLowerCase() !== teamFilter.toLowerCase()) continue;
if (stateFilter && state?.name?.toLowerCase() !== stateFilter.toLowerCase()) continue;
console.log(`--- ${issue.identifier} ---`);
console.log(`Title: ${issue.title}`);
console.log(`State: ${state?.name || "Unknown"}`);
console.log(`Priority: ${issue.priorityLabel}`);
console.log(`Team: ${team?.key || "?"} | Assignee: ${assignee?.name || "Unassigned"}`);
console.log(`Created: ${formatDate(issue.createdAt)} | Updated: ${formatDate(issue.updatedAt)}`);
if (issue.description) console.log(`Description: ${truncate(issue.description, 200)}`);
console.log(`URL: ${issue.url}`);
console.log("");
}
if (results.nodes.length === 0) {
console.log("No results found.");
}
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env node
// List all Linear teams
// Usage: linear-teams.js
import { getClient } from "./lib.js";
const client = getClient();
const teams = await client.teams();
console.log("Teams:");
for (const team of teams.nodes) {
console.log(` ${team.key.padEnd(8)} ${team.name}`);
}
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env node
// Update an existing Linear issue
// Usage: linear-update.js <identifier> [--title <title>] [--state <name>] [--priority <0-4>] [--assignee <name|me>] [--description <text>]
import { getClient } from "./lib.js";
const args = process.argv.slice(2);
const identifier = args[0];
if (!identifier || identifier.startsWith("--")) {
console.log("Usage: linear-update.js <identifier> [options]");
console.log("\nOptions:");
console.log(" --title <title> New title");
console.log(" --state <name> New state (e.g. 'In Progress')");
console.log(" --priority <0-4> New priority");
console.log(" --assignee <name|me> New assignee");
console.log(" --description <text> New description");
process.exit(1);
}
args.shift();
function extractArg(flag) {
const idx = args.indexOf(flag);
if (idx !== -1 && args[idx + 1]) {
const val = args[idx + 1];
args.splice(idx, 2);
return val;
}
return null;
}
const title = extractArg("--title");
const stateName = extractArg("--state");
const priority = extractArg("--priority");
const assigneeName = extractArg("--assignee");
const description = extractArg("--description");
const client = getClient();
// Find the issue
const results = await client.searchIssues(identifier, { first: 1 });
const issue = results.nodes[0];
if (!issue) {
console.error(`Issue '${identifier}' not found.`);
process.exit(1);
}
const input = {};
if (title) input.title = title;
if (description) input.description = description;
if (priority) input.priority = parseInt(priority, 10);
// Resolve state
if (stateName) {
const team = await issue.team;
const states = await team.states();
const state = states.nodes.find(
(s) => s.name.toLowerCase() === stateName.toLowerCase()
);
if (state) input.stateId = state.id;
else {
console.error(`State '${stateName}' not found. Available states:`);
for (const s of states.nodes) console.error(` - ${s.name}`);
process.exit(1);
}
}
// Resolve assignee
if (assigneeName) {
if (assigneeName.toLowerCase() === "me") {
const me = await client.viewer;
input.assigneeId = me.id;
} else {
const users = await client.users({ filter: { name: { containsIgnoreCase: assigneeName } } });
if (users.nodes[0]) input.assigneeId = users.nodes[0].id;
else {
console.error(`User '${assigneeName}' not found.`);
process.exit(1);
}
}
}
if (Object.keys(input).length === 0) {
console.log("No updates specified. Use --title, --state, --priority, --assignee, or --description.");
process.exit(1);
}
await client.updateIssue(issue.id, input);
console.log(`Updated ${issue.identifier}: ${issue.title}`);
console.log(`URL: ${issue.url}`);
+107
View File
@@ -0,0 +1,107 @@
{
"name": "linear-skill",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "linear-skill",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@linear/sdk": "^37.0.0"
}
},
"node_modules/@graphql-typed-document-node/core": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
"integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
"license": "MIT",
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@linear/sdk": {
"version": "37.0.0",
"resolved": "https://registry.npmjs.org/@linear/sdk/-/sdk-37.0.0.tgz",
"integrity": "sha512-EAZCXtV414Nwtvrwn7Ucu3E8BbYYKsc3HqZCGf1mHUE7FhZGtfISu295DOVv89WhhXlp2N344EMg3K0nnhLxtA==",
"license": "MIT",
"dependencies": {
"@graphql-typed-document-node/core": "^3.1.0",
"graphql": "^15.4.0",
"isomorphic-unfetch": "^3.1.0"
},
"engines": {
"node": ">=12.x",
"yarn": "1.x"
}
},
"node_modules/graphql": {
"version": "15.10.1",
"resolved": "https://registry.npmjs.org/graphql/-/graphql-15.10.1.tgz",
"integrity": "sha512-BL/Xd/T9baO6NFzoMpiMD7YUZ62R6viR5tp/MULVEnbYJXZA//kRNW7J0j1w/wXArgL0sCxhDfK5dczSKn3+cg==",
"license": "MIT",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/isomorphic-unfetch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz",
"integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==",
"license": "MIT",
"dependencies": {
"node-fetch": "^2.6.1",
"unfetch": "^4.2.0"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/unfetch": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
"integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==",
"license": "MIT"
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
}
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"name": "linear-skill",
"version": "1.0.0",
"type": "module",
"description": "Linear API skill for pi - manage issues, projects, and teams",
"license": "MIT",
"dependencies": {
"@linear/sdk": "^37.0.0"
}
}