Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

templates.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

templates.tsBlame86 lines · 1 contributor
24cf2caClaude1/**
2 * Issue + PR template loader. Looks for the standard locations and
3 * returns the first match. Fails silently (returns null) so new-issue /
4 * new-PR forms always render — templates are a convenience, not a
5 * requirement.
6 */
7
8import { getBlob, getDefaultBranch } from "../git/repository";
9
10const ISSUE_PATHS = [
11 ".github/ISSUE_TEMPLATE.md",
12 ".github/issue_template.md",
13 ".gluecron/ISSUE_TEMPLATE.md",
14 "ISSUE_TEMPLATE.md",
15 "docs/ISSUE_TEMPLATE.md",
16];
17
18const PR_PATHS = [
19 ".github/PULL_REQUEST_TEMPLATE.md",
20 ".github/pull_request_template.md",
21 ".gluecron/PULL_REQUEST_TEMPLATE.md",
22 "PULL_REQUEST_TEMPLATE.md",
23 "docs/PULL_REQUEST_TEMPLATE.md",
24];
25
26const MAX_TEMPLATE_BYTES = 16 * 1024;
27
28async function loadFirst(
29 owner: string,
30 repo: string,
31 ref: string,
32 paths: string[]
33): Promise<string | null> {
34 for (const p of paths) {
35 try {
36 const blob = await getBlob(owner, repo, ref, p);
37 if (blob && !blob.isBinary && blob.content) {
38 // Guard against someone committing a 5MB template.
39 if (blob.content.length > MAX_TEMPLATE_BYTES) {
40 return blob.content.slice(0, MAX_TEMPLATE_BYTES);
41 }
42 return stripFrontmatter(blob.content);
43 }
44 } catch {
45 // keep looking
46 }
47 }
48 return null;
49}
50
51/**
52 * GitHub-style templates can have YAML frontmatter (---\nname: ...\n---).
53 * Strip it — we don't use the name/about fields here.
54 */
55function stripFrontmatter(content: string): string {
56 if (!content.startsWith("---\n")) return content;
57 const end = content.indexOf("\n---", 4);
58 if (end < 0) return content;
59 return content.slice(end + 4).replace(/^\n+/, "");
60}
61
62export async function loadIssueTemplate(
63 owner: string,
64 repo: string
65): Promise<string | null> {
66 try {
67 const ref = (await getDefaultBranch(owner, repo)) || "HEAD";
68 return await loadFirst(owner, repo, ref, ISSUE_PATHS);
69 } catch {
70 return null;
71 }
72}
73
74export async function loadPrTemplate(
75 owner: string,
76 repo: string
77): Promise<string | null> {
78 try {
79 const ref = (await getDefaultBranch(owner, repo)) || "HEAD";
80 return await loadFirst(owner, repo, ref, PR_PATHS);
81 } catch {
82 return null;
83 }
84}
85
86export { stripFrontmatter as _stripFrontmatterForTest };