Linearis follows TypeScript-first development practices with strict typing, modular architecture, and GraphQL-optimized design patterns. Development emphasizes code clarity, maintainability, and efficient GraphQL operations for optimal Linear integration performance.
The codebase uses modern ES modules, async/await patterns throughout, and leverages TypeScript's type system for compile-time safety. All development follows the principle of smart defaults with explicit user control when needed. Recent optimization work focuses on replacing SDK-heavy operations with direct GraphQL queries.
Strict Typing - All files use comprehensive TypeScript interfaces:
// From src/utils/linear-types.d.ts lines 1-41
export interface LinearIssue {
id: string;
identifier: string;
title: string;
description?: string;
state: { id: string; name: string };
// ... complete type definitions
}Interface-Driven Development - src/utils/linear-types.d.ts (lines 63-96):
- CreateIssueArgs interface for issue creation parameters
- UpdateIssueArgs interface for issue updates
- SearchIssuesArgs interface for search operations
Consistent Promise Handling - Throughout src/utils/linear-service.ts:
// Example from lines 128-137 - Parallel API calls
const [state, team, assignee, project, labels] = await Promise.all([
issue.state,
issue.team,
issue.assignee,
issue.project,
issue.labels(),
]);Error Handling Pattern - src/utils/output.ts (lines 23-33):
export function handleAsyncCommand(
asyncFn: (...args: any[]) => Promise<void>,
): (...args: any[]) => Promise<void> {
return async (...args: any[]) => {
try {
await asyncFn(...args);
} catch (error) {
outputError(error instanceof Error ? error : new Error(String(error)));
}
};
}Import/Export Style - All files use ES module syntax:
- src/main.ts (lines 3-5) - Named imports with .js extensions
- src/utils/auth.ts (lines 18, 38) - Interface exports and async functions
- All imports use .js extensions for ES module compatibility
Commander.js Integration - src/commands/issues.ts (lines 9-16):
export function setupIssuesCommands(program: Command): void {
const issues = program.command("issues")
.description("Issue operations");
// Show help when no subcommand
issues.action(() => {
issues.help();
});Authentication Integration - src/utils/linear-service.ts (lines 479-484):
export async function createLinearService(
options: CommandOptions,
): Promise<LinearService> {
const apiToken = await getApiToken(options);
return new LinearService(apiToken);
}UUID Validation Helper - src/utils/uuid.ts:
// Generic UUID validation using proper regex
export function isUuid(value: string): boolean {
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
return uuidRegex.test(value);
}Flexible Identifier Handling - src/utils/linear-service.ts (lines 196-227):
// Check if UUID or identifier format using helper
if (isUuid(issueId)) {
issue = await this.client.issue(issueId);
} else {
// Parse team-number format like "ABC-123"
const parts = issueId.split("-");
// ... resolve to internal UUID
}Single Query Strategy - Used throughout GraphQL service layer:
// From src/utils/graphql-issues-service.ts lines 32-46
async getIssues(limit: number = 25): Promise<LinearIssue[]> {
const result = await this.graphQLService.rawRequest(GET_ISSUES_QUERY, {
first: limit,
orderBy: "updatedAt" as any,
});
// Complete data in single response - no N+1 queries
}Batch Resolution Pattern - Resolve multiple IDs in single operation:
// From src/utils/graphql-issues-service.ts lines 294-301
const resolveResult = await this.graphQLService.rawRequest(
BATCH_RESOLVE_FOR_CREATE_QUERY,
{ teamName, projectName, labelNames },
);
// All IDs resolved in single queryEnhanced Label Management - Supporting both adding and overwriting modes:
// From src/utils/graphql-issues-service.ts lines 188-196
if (labelMode === "adding") {
// Merge with current labels
finalLabelIds = [...new Set([...currentIssueLabels, ...resolvedLabels])];
} else {
// Overwrite mode - replace all existing labels
finalLabelIds = resolvedLabels;
}- Define Interfaces - Add to src/utils/linear-types.d.ts
- Create GraphQL Queries - Add optimized queries to src/queries/
- Implement GraphQL Service Methods - Add to src/utils/graphql-issues-service.ts or create new GraphQL service
- Create Command Handler - Add to appropriate src/commands/ file
- Register Command - Import and setup in src/main.ts
- Design Query Strategy - Single query vs batch resolution approach
- Create Query Fragments - Reuse existing fragments from src/queries/common.ts
- Implement Service Method - Use GraphQLService for raw execution
- Add Error Handling - Transform GraphQL errors to user-friendly messages
- Test Performance - Compare against SDK-based approach for improvements
Example Command Addition Pattern - src/commands/issues.ts (lines 138-152):
issues.command("read <issueId>")
.description(
"Get issue details (supports both UUID and identifier like ABC-123)",
)
.action(
handleAsyncCommand(
async (issueId: string, options: any, command: Command) => {
const service = await createLinearService(
command.parent!.parent!.opts(),
);
const result = await service.getIssueById(issueId);
outputSuccess(result);
},
),
);Development Mode - package.json (line 14):
# Run with TypeScript execution via tsx (development only)
npm start issues list -l 5
# Direct execution for debugging
npx tsx src/main.ts --api-token <token> issues read ABC-123Production Build Workflow:
# Clean and compile for production
npm run clean && npm run build
# Test compiled output (creates executable dist/main.js)
chmod +x dist/main.js
./dist/main.js issues list -l 5
# Time comparison (compiled is significantly faster)
time ./dist/main.js --help
time npx tsx src/main.ts --helpMultiple Token Sources - src/utils/auth.ts (lines 18-38):
- Command flag:
--api-token <token> - Environment:
LINEAR_API_TOKEN=<token> - File:
echo "<token>" > ~/.linear_api_token
Consistent Error Response - src/utils/output.ts (lines 13-16):
export function outputError(error: Error): void {
console.error(JSON.stringify({ error: error.message }, null, 2));
process.exit(1);
}Service Layer - src/utils/ directory:
- graphql-service.ts - GraphQL client wrapper with batch operations
- graphql-issues-service.ts - Optimized GraphQL issue operations
- linear-service.ts - Legacy SDK-based business logic and fallback operations
- auth.ts - Authentication handling
- output.ts - Response formatting
- linear-types.d.ts - Type definitions
- uuid.ts - UUID validation utilities
Command Layer - src/commands/ directory:
- issues.ts - Issue-related commands with enhanced label and parent management
- projects.ts - Project-related commands
- comments.ts - Comment operations with lightweight ID resolution
- teams.ts - Team operations (list) with workspace team discovery
- users.ts - User operations (list) with active user filtering
- Pattern: Each domain gets its own command file
Query Layer - src/queries/ directory:
- common.ts - Reusable GraphQL fragments
- issues.ts - Optimized issue-specific GraphQL queries and mutations
- index.ts - Query exports and organization
Functions - camelCase with descriptive names:
getApiToken(),createLinearService(),handleAsyncCommand()- Service methods:
getIssues(),searchIssues(),createIssue()
Interfaces - PascalCase with descriptive prefixes:
LinearIssue,LinearProjectfor data modelsCreateIssueArgs,UpdateIssueArgsfor operation parameters
Type Safety - Every function parameter and return type explicitly typed Error Boundaries - All async operations wrapped with error handling
GraphQL First - New operations use GraphQL service for optimal performance User Experience - Smart defaults with explicit override options Build Automation - npm prepare script ensures consistent builds
Automated Building - package.json (line 13):
# prepare script runs automatically during install
npm install # Triggers: npm run clean && npm run buildTypeScript Configuration - tsconfig.json optimizations:
- Target: ES2023 for modern Node.js features
- Output: dist/ directory with declaration files
- Remove comments and source maps for production
- Strict mode enabled for type safety
ES Module Imports - Always use .js extensions in imports, even for .ts files Authentication Testing - Use token file method for local development GraphQL vs SDK - Prefer GraphQL service for new operations, use SDK for fallbacks
API Rate Limits - Linear API has reasonable limits, but GraphQL batch operations help Development vs Production - Use tsx for development, compiled JS for production (significantly faster) Missing dist/ - Run npm install or npm run build to create executable compiled output
Build creates executable - npm run build automatically makes dist/main.js executable