UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

120 lines (119 loc) 3.77 kB
#!/usr/bin/env node import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { readFileSync } from "fs"; import { join } from "path"; async function showLinearSummary() { const tokensPath = join(process.cwd(), ".stackmemory", "linear-tokens.json"); let accessToken; try { const tokensData = readFileSync(tokensPath, "utf8"); const tokens = JSON.parse(tokensData); accessToken = tokens.accessToken; } catch { console.error( "\u274C Failed to load Linear tokens. Please run: stackmemory linear setup" ); process.exit(1); } const linearApiUrl = "https://api.linear.app/graphql"; async function graphqlRequest(query, variables = {}) { const response = await fetch(linearApiUrl, { method: "POST", headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ query, variables }) }); if (!response.ok) { throw new Error( `Linear API error: ${response.status} ${response.statusText}` ); } const result = await response.json(); if (result.errors) { throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`); } return result.data; } const issuesQuery = ` query { issues(first: 250) { nodes { identifier title state { name type } createdAt updatedAt } } } `; const data = await graphqlRequest(issuesQuery); const issues = data.issues.nodes; const grouped = /* @__PURE__ */ new Map(); for (const issue of issues) { const stateType = issue.state.type; if (!grouped.has(stateType)) { grouped.set(stateType, []); } const stateIssues = grouped.get(stateType); if (stateIssues) { stateIssues.push(issue); } } console.log("\n\u{1F4CA} Linear Task Summary\n"); console.log("=".repeat(60)); const stateOrder = [ "backlog", "unstarted", "started", "completed", "canceled" ]; for (const state of stateOrder) { const stateIssues = grouped.get(state) || []; if (stateIssues.length === 0) continue; const emoji = { backlog: "\u{1F4CB}", unstarted: "\u23F3", started: "\u{1F504}", completed: "\u2705", canceled: "\u274C" }[state] || "\u26AA"; console.log( ` ${emoji} ${state.toUpperCase()} (${stateIssues.length} tasks)` ); console.log("-".repeat(40)); const recent = stateIssues.sort( (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime() ).slice(0, state === "canceled" ? 20 : 5); for (const issue of recent) { const updatedDate = new Date(issue.updatedAt).toLocaleDateString(); console.log( ` ${issue.identifier.padEnd(10)} ${issue.title.slice(0, 50).padEnd(50)} ${updatedDate}` ); } if (stateIssues.length > recent.length) { console.log(` ... and ${stateIssues.length - recent.length} more`); } } console.log("\n" + "=".repeat(60)); console.log("\n\u{1F4C8} Total Issues: " + issues.length); const activeCount = (grouped.get("started")?.length ?? 0) + (grouped.get("unstarted")?.length ?? 0) + (grouped.get("backlog")?.length ?? 0); console.log(" Active: " + activeCount); console.log(" Completed: " + (grouped.get("completed")?.length ?? 0)); console.log(" Canceled: " + (grouped.get("canceled")?.length ?? 0)); } showLinearSummary().catch((error) => { console.error("\u274C Error:", error); process.exit(1); }); //# sourceMappingURL=show-linear-summary.js.map