| name | linear-debug-bundle |
| description | Comprehensive debugging toolkit for Linear integrations.
Use when setting up logging, tracing API calls,
or building debug utilities for Linear.
Trigger with phrases like "debug linear integration", "linear logging",
"trace linear API", "linear debugging tools", "linear troubleshooting".
|
| allowed-tools | Read, Write, Edit, Grep, Bash(node:*), Bash(npx:*) |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Linear Debug Bundle
Overview
Comprehensive debugging tools for Linear API integrations.
Prerequisites
- Linear SDK configured
- Node.js environment
- Optional: logging library (pino, winston)
Instructions
Step 1: Create Debug Client Wrapper
import { LinearClient } from "@linear/sdk";
interface DebugOptions {
logRequests?: boolean;
logResponses?: boolean;
logErrors?: boolean;
onRequest?: (query: string, variables: unknown) => void;
onResponse?: (data: unknown, duration: number) => void;
onError?: (error: Error, duration: number) => void;
}
export function createDebugClient(
apiKey: string,
options: DebugOptions = {}
): LinearClient {
const {
logRequests = true,
logResponses = true,
logErrors = true,
} = options;
const client = new LinearClient({
apiKey,
fetch: async (url, init) => {
const start = Date.now();
const body = init?.body ? JSON.parse(init.body as string) : null;
if (logRequests && body) {
console.log("[Linear Request]", {
query: body.query?.slice(0, 100) + "...",
variables: body.variables,
});
options.onRequest?.(body.query, body.variables);
}
try {
const response = await fetch(url, init);
const duration = Date.now() - start;
const data = await response.clone().json();
if (logResponses) {
console.log("[Linear Response]", {
duration: `${duration}ms`,
hasErrors: !!data.errors,
dataKeys: data.data ? Object.keys(data.data) : [],
});
options.onResponse?.(data, duration);
}
return response;
} catch (error) {
const duration = Date.now() - start;
if (logErrors) {
console.error("[Linear Error]", {
duration: `${duration}ms`,
error: error instanceof Error ? error.message : error,
});
options.onError?.(error as Error, duration);
}
throw error;
}
},
});
return client;
}
Step 2: Request Tracer
interface TraceEntry {
id: string;
operation: string;
startTime: Date;
endTime?: Date;
duration?: number;
success: boolean;
error?: string;
metadata?: Record<string, unknown>;
}
class LinearTracer {
private traces: TraceEntry[] = [];
private maxTraces = 100;
startTrace(operation: string, metadata?: Record<string, unknown>): string {
const id = crypto.randomUUID();
this.traces.push({
id,
operation,
startTime: new Date(),
success: false,
metadata,
});
if (this.traces.length > this.) {
. = ..(-.);
}
id;
}
(: , : , ?: ): {
trace = ..( t. === id);
(trace) {
trace. = ();
trace. = trace..() - trace..();
trace. = success;
trace. = error;
}
}
(): [] {
[....];
}
(thresholdMs = ): [] {
..( (t. ?? ) > thresholdMs);
}
(): [] {
..( !t.);
}
(): <, > {
completed = ..( t. !== );
durations = completed.( t.!);
{
: ..,
: completed.,
: .().,
: durations.
? .(durations.( a + b, ) / durations.)
: ,
: .(...durations, ),
};
}
}
tracer = ();
Step 3: Health Check Utility
import { LinearClient } from "@linear/sdk";
interface HealthCheckResult {
healthy: boolean;
latencyMs: number;
user?: { name: string; email: string };
teams?: number;
error?: string;
timestamp: Date;
}
export async function checkLinearHealth(
client: LinearClient
): Promise<HealthCheckResult> {
const start = Date.now();
try {
const [viewer, teams] = await Promise.all([
client.viewer,
client.teams(),
]);
return {
healthy: true,
latencyMs: Date.now() - start,
user: { name: viewer.name, email: viewer.email },
teams: teams..,
: (),
};
} (error) {
{
: ,
: .() - start,
: error ? error. : ,
: (),
};
}
}
() {
(: , : ) => {
result = (client);
res.(result. ? : ).(result);
};
}
Step 4: Debug Console Commands
import { LinearClient } from "@linear/sdk";
import readline from "readline";
const client = new LinearClient({ apiKey: process.env.LINEAR_API_KEY! });
const commands: Record<string, () => Promise<void>> = {
async me() {
const viewer = await client.viewer;
console.log("Current user:", viewer.name, viewer.email);
},
async teams() {
const teams = await client.teams();
console.log("Teams:");
teams.nodes.forEach(t => console.log(` ${t.key}: ${t.name}`));
},
async issues() {
issues = client.({ : });
.();
issues..( .());
},
() {
teams = client.();
( team teams.) {
states = team.();
.();
states..( .());
}
},
() {
.();
.();
},
};
rl = readline.({
: process.,
: process.,
: ,
});
rl.();
rl.(, (line) => {
cmd = line.().();
(cmd === ) {
rl.();
;
}
(commands[cmd]) {
commands[cmd]();
} {
.();
}
rl.();
});
Step 5: Environment Validator
interface ValidationResult {
valid: boolean;
errors: string[];
warnings: string[];
}
export function validateLinearEnv(): ValidationResult {
const errors: string[] = [];
const warnings: string[] = [];
if (!process.env.LINEAR_API_KEY) {
errors.push("LINEAR_API_KEY is not set");
} else if (!process.env.LINEAR_API_KEY.startsWith("lin_api_")) {
errors.push("LINEAR_API_KEY has invalid format (should start with lin_api_)");
}
if (!process.env.LINEAR_WEBHOOK_SECRET) {
warnings.push("LINEAR_WEBHOOK_SECRET not set (webhooks won't be verified)");
}
if (process.env.NODE_ENV === "production" && !process.env.LINEAR_API_KEY?.includes("prod")) {
warnings.push();
}
{
: errors. === ,
errors,
warnings,
};
}
result = ();
(!result.) {
.(, result.);
}
result..( .(, w));
Output
- Debug client with request/response logging
- Request tracer with performance metrics
- Health check endpoint
- Interactive debug console
- Environment validator
Error Handling
| Error | Cause | Solution |
|---|
Circular JSON | Logging full Linear objects | Use selective logging |
Memory leak | Unbounded trace storage | Set maxTraces limit |
Missing env | Validation failed | Check environment setup |
Resources
Next Steps
Learn rate limiting strategies with linear-rate-limits.