linearcreate
Advanced tools
+16
-7
| { | ||
| "name": "linearcreate", | ||
| "version": "1.0.11", | ||
| "version": "1.0.12", | ||
| "description": "A CLI tool to create Linear issues and generate branch names", | ||
@@ -11,11 +11,9 @@ "main": "dist/lc.js", | ||
| "build": "tsc", | ||
| "prepare": "npm run build", | ||
| "start": "ts-node src/lc.ts", | ||
| "start:dev": "ts-node src/lc.ts", | ||
| "start:prod": "npm run build && node dist/lc.js", | ||
| "prepublishOnly": "npm run build", | ||
| "postinstall": "node dist/setup.js" | ||
| "prepublishOnly": "npm run build" | ||
| }, | ||
| "dependencies": { | ||
| "@linear/sdk": "^27.0.0", | ||
| "clipboardy": "^3.0.0", | ||
| "clipboardy": "2.3.0", | ||
| "dotenv": "^16.3.1", | ||
@@ -26,2 +24,3 @@ "open": "^9.1.0", | ||
| "devDependencies": { | ||
| "@types/node": "^20.4.8", | ||
| "@types/yargs": "^17.0.33", | ||
@@ -31,4 +30,14 @@ "ts-node": "^10.9.1", | ||
| "eslint": "^8.57.0", | ||
| "eslint-config-next": "^14.2.5" | ||
| "eslint-config-prettier": "^9.1.0", | ||
| "eslint-plugin-prettier": "^5.1.3", | ||
| "prettier": "^3.0.0" | ||
| }, | ||
| "files": [ | ||
| "dist", | ||
| "README.md", | ||
| "LICENSE" | ||
| ], | ||
| "engines": { | ||
| "node": ">=14.0.0" | ||
| }, | ||
| "keywords": [ | ||
@@ -35,0 +44,0 @@ "linear", |
Sorry, the diff of this file is not supported yet
-136
| #!/usr/bin/env node | ||
| import { LinearClient } from "@linear/sdk"; | ||
| import clipboardy from "clipboardy"; | ||
| import dotenv from "dotenv"; | ||
| import open from "open"; | ||
| import yargs from "yargs"; | ||
| import { hideBin } from "yargs/helpers"; | ||
| import path from 'path'; | ||
| import { runSetup } from './setup'; | ||
| // Load environment variables from .env file | ||
| dotenv.config({ path: path.join(process.cwd(), '.env') }); | ||
| const apiKey = process.env.LINEAR_API_KEY; | ||
| const defaultTeamStub = process.env.DEFAULT_TEAM_STUB; | ||
| const userId = process.env.USER_ID; | ||
| if (!apiKey || !defaultTeamStub || !userId) { | ||
| console.error('Missing environment variables. Please run "lc setup" to configure.'); | ||
| process.exit(1); | ||
| } | ||
| const linearClient = new LinearClient({ apiKey }); | ||
| const userIdString = userId as string; | ||
| async function createIssueWithBranch(title: string, teamStub: string, description?: string) { | ||
| try { | ||
| // Fetch the organization | ||
| const organization = await linearClient.organization; | ||
| // Fetch the teams | ||
| const teams = await organization.teams(); | ||
| const team = teams.nodes.find( | ||
| (t) => t.key.toLowerCase() === teamStub.toLowerCase() | ||
| ); | ||
| if (!team) { | ||
| throw new Error(`No team found with stub: ${teamStub}`); | ||
| } | ||
| // Create the issue | ||
| const issuePayload = await linearClient.createIssue({ | ||
| title, | ||
| teamId: team.id, | ||
| assigneeId: userId, | ||
| description: description, | ||
| }); | ||
| if (!issuePayload.success || !issuePayload.issue) { | ||
| throw new Error("Failed to create issue"); | ||
| } | ||
| const issue = await issuePayload.issue; | ||
| // Generate the branch name | ||
| const sanitizedTitle = title | ||
| .toLowerCase() | ||
| .replace(/[^a-z0-9]+/g, "-") | ||
| .replace(/^-+|-+$/g, "") | ||
| .substring(0, 50); // Limit length to 50 characters | ||
| // Fetch the user | ||
| const user = await linearClient.user(userIdString); | ||
| if (!user) { | ||
| throw new Error("Failed to fetch user"); | ||
| } | ||
| const branchName = `${user.name.toLowerCase().replace(/\s+/g, '')}/${issue.identifier.toLowerCase()}-${sanitizedTitle}`; | ||
| return { issue, branchName }; | ||
| } catch (error) { | ||
| throw error; | ||
| } | ||
| } | ||
| async function main() { | ||
| const argv = await yargs(hideBin(process.argv)) | ||
| .command( | ||
| "$0 <ticketName>", | ||
| "Create a new Linear issue and generate a branch name", | ||
| (yargs: any) => { | ||
| return yargs | ||
| .positional("ticketName", { | ||
| describe: "Name of the ticket", | ||
| type: "string", | ||
| demandOption: true, | ||
| }) | ||
| .option("teamstub", { | ||
| alias: "t", | ||
| type: "string", | ||
| description: "Team stub (e.g., bknd, frtd)", | ||
| default: defaultTeamStub, | ||
| }) | ||
| .option("open", { | ||
| alias: "o", | ||
| type: "boolean", | ||
| description: "Open the ticket in Linear", | ||
| default: false, | ||
| }) | ||
| .option("description", { | ||
| alias: "d", | ||
| type: "string", | ||
| description: "Description for the Linear issue", | ||
| }); | ||
| } | ||
| ) | ||
| .command('setup', 'Run the setup process again', {}, async () => { | ||
| await runSetup(); | ||
| }) | ||
| .help() | ||
| .parse(); | ||
| try { | ||
| const { issue, branchName } = await createIssueWithBranch( | ||
| argv.ticketName as string, | ||
| argv.teamstub as string, | ||
| argv.description as string | undefined | ||
| ); | ||
| if (argv.open) { | ||
| console.log(`Opening issue in Linear: ${issue.url}`); | ||
| await open(issue.url); | ||
| } else { | ||
| await clipboardy.write(branchName); | ||
| console.log(`Branch name copied to clipboard: ${branchName}`); | ||
| } | ||
| console.log(`Issue created: ${issue.title} (${issue.identifier})`); | ||
| } catch (error) { | ||
| console.error("Error:", error); | ||
| process.exit(1); | ||
| } | ||
| } | ||
| main(); |
-33
| import fs from 'fs'; | ||
| import readline from 'readline'; | ||
| import path from 'path'; | ||
| const rl = readline.createInterface({ | ||
| input: process.stdin, | ||
| output: process.stdout | ||
| }); | ||
| const envPath = path.join(process.cwd(), '.env'); | ||
| console.log('Welcome to linearcreate setup!'); | ||
| export function runSetup() { | ||
| return new Promise<void>((resolve) => { | ||
| rl.question('Enter your Linear API key: ', (apiKey) => { | ||
| rl.question('Enter your default team stub: ', (teamStub) => { | ||
| rl.question('Enter your user ID: ', (userId) => { | ||
| const envContent = `LINEAR_API_KEY=${apiKey}\nDEFAULT_TEAM_STUB=${teamStub}\nUSER_ID=${userId}\n`; | ||
| fs.writeFileSync(envPath, envContent); | ||
| console.log('.env file created successfully!'); | ||
| rl.close(); | ||
| resolve(); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| } | ||
| if (require.main === module) { | ||
| runSetup(); | ||
| } |
| { | ||
| "compilerOptions": { | ||
| "target": "ES2019", | ||
| "module": "CommonJS", | ||
| "strict": true, | ||
| "esModuleInterop": true, | ||
| "forceConsistentCasingInFileNames": true, | ||
| "skipLibCheck": true, | ||
| "outDir": "dist", | ||
| "sourceMap": true | ||
| }, | ||
| "include": ["src/**/*"] | ||
| } | ||
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed or built. Malicious packages often use scripts that run automatically to execute payloads or fetch additional code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
0
-100%12
-25%2
-33.33%21138
-18.8%8
60%8
-33.33%250
-38.12%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
Updated