@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
JavaScript
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