Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing | 1 | /** |
| 2 | * g1t's additions to GitHub flavoured markdown. They run on the HTML tree | |
| 3 | * after it has been sanitized, so what they add is trusted and what the | |
| 4 | * author wrote never is. | |
| 5 | */ | |
| 6 | ||
| 7 | type Text = { type: "text"; value: string }; | |
| 8 | type Element = { | |
| 9 | type: "element"; | |
| 10 | tagName: string; | |
| 11 | properties: Record<string, unknown>; | |
| 12 | children: Node[]; | |
| 13 | }; | |
| 14 | /** A node of the HTML tree, loosely: only what these plugins read. */ | |
| 15 | type Node = { type: string; value?: string; tagName?: string; properties?: Record<string, unknown>; children?: Node[] }; | |
| 16 | ||
| 17 | /** The repository a document belongs to, so `#12` can name its issues. */ | |
| 18 | export type MarkdownRepo = { namespace: string; name: string }; | |
| 19 | ||
| 20 | /** The repository a site path like `/acme/web` names. */ | |
| 21 | export function repoAt(base?: string): MarkdownRepo | undefined { | |
| 22 | const [namespace, name] = (base ?? "").split("/").filter(Boolean); | |
| 23 | return namespace && name ? { namespace, name } : undefined; | |
| 24 | } | |
| 25 | ||
| 26 | export const ALERTS = ["note", "tip", "important", "warning", "caution"] as const; | |
| 27 | export type AlertKind = (typeof ALERTS)[number]; | |
| 28 | ||
| 29 | const ALERT_MARKER = /^\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*\n?/i; | |
| 30 | ||
| 31 | function isElement(node: Node, tag?: string): node is Element { | |
| 32 | return node.type === "element" && (tag == null || (node as Element).tagName === tag); | |
| 33 | } | |
| 34 | ||
| 35 | /** | |
| 36 | * GitHub's alerts: a quote that starts with `[!NOTE]`, `[!TIP]`, | |
| 37 | * `[!IMPORTANT]`, `[!WARNING]` or `[!CAUTION]` becomes a callout. | |
| 38 | */ | |
| 39 | export function rehypeAlerts() { | |
| 40 | return (tree: Node) => { | |
| 41 | const walk = (node: Node) => { | |
| 42 | for (const child of node.children ?? []) { | |
| 43 | if (isElement(child, "blockquote")) { | |
| 44 | const first = child.children.find((c): c is Element => isElement(c, "p")); | |
| 45 | const text = first?.children[0]; | |
| 46 | const match = text?.type === "text" ? ALERT_MARKER.exec((text as Text).value) : null; | |
| 47 | if (first && text && match) { | |
| 48 | (text as Text).value = (text as Text).value.slice(match[0].length); | |
| 49 | // A marker on a line of its own leaves an empty paragraph behind. | |
| 50 | if (first.children.length === 1 && !(text as Text).value.trim()) { | |
| 51 | child.children = child.children.filter((c) => c !== first); | |
| 52 | } | |
| 53 | child.properties = { ...child.properties, dataAlert: match[1]!.toLowerCase() }; | |
| 54 | } | |
| 55 | } | |
| 56 | walk(child); | |
| 57 | } | |
| 58 | }; | |
| 59 | walk(tree); | |
| 60 | }; | |
| 61 | } | |
| 62 | ||
| Merge branch 'worktree-agent-a8385d293d42c913a' | 63 | /** |
| 64 | * Where `@g1t` leads: g1t's own agent, which has no profile. Never `/g1t`, | |
| 65 | * which staff made an alias of Flagon, Inc.'s workspace: mentioning g1t | |
| 66 | * always means the agent. | |
| 67 | */ | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 68 | export const G1T_MENTION_HREF = "https://docs.g1t.sh/"; |
| Merge branch 'worktree-agent-a8385d293d42c913a' | 69 | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 70 | /** `owner/repo#12`, `#12`, `@workspace/team`, `@name` and commit hashes, in one pass. */ |
| Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing | 71 | const REFERENCE = |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 72 | /(?<![\w/@#])(?:([a-z0-9][a-z0-9-]*\/[a-z0-9._-]+)#(\d+)|#(\d+)|@([a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){0,38})\/([a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){0,59})|@([a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){0,38})|([0-9a-f]{7,40}))(?![\w-])/gi; |
| Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing | 73 | |
| 74 | function link(href: string, text: string, kind: string): Element { | |
| 75 | return { | |
| 76 | type: "element", | |
| 77 | tagName: "a", | |
| 78 | properties: { href, dataRef: kind }, | |
| 79 | children: [{ type: "text", value: text }], | |
| 80 | }; | |
| 81 | } | |
| 82 | ||
| 83 | /** | |
| 84 | * Links what a forge's text refers to: `#12` to an issue or pull request | |
| 85 | * in this repository (the issue page sends a pull request's number on to | |
| Merge branch 'worktree-agent-a8385d293d42c913a' | 86 | * it), `owner/repo#12` to one elsewhere, `@name` to a person or workspace |
| 87 | * (`@g1t` to how g1t's agent works), and a commit hash to its commit. | |
| 88 | * Nothing inside code or a link changes. | |
| Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing | 89 | */ |
| 90 | export function rehypeReferences(options: { repo?: MarkdownRepo }) { | |
| 91 | const { repo } = options; | |
| 92 | return (tree: Node) => { | |
| 93 | const walk = (node: Node) => { | |
| 94 | const children = node.children; | |
| 95 | if (!children) return; | |
| 96 | for (let index = 0; index < children.length; index++) { | |
| 97 | const child = children[index]!; | |
| 98 | if (isElement(child)) { | |
| 99 | if (child.tagName === "a" || child.tagName === "code" || child.tagName === "pre") continue; | |
| 100 | walk(child); | |
| 101 | continue; | |
| 102 | } | |
| 103 | if (child.type !== "text") continue; | |
| 104 | const value = (child as Text).value; | |
| 105 | const parts: Node[] = []; | |
| 106 | let last = 0; | |
| 107 | for (const match of value.matchAll(REFERENCE)) { | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 108 | const [whole, other, otherNumber, number, workspace, team, name, hash] = match; |
| Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing | 109 | let replacement: Element | null = null; |
| 110 | if (other && otherNumber) { | |
| 111 | replacement = link(`/${other}/issues/${otherNumber}`, whole, "issue"); | |
| 112 | } else if (number && repo) { | |
| 113 | replacement = link(`/${repo.namespace}/${repo.name}/issues/${number}`, whole, "issue"); | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 114 | } else if (workspace && team) { |
| 115 | replacement = link(`/${workspace.toLowerCase()}/-/teams/${team.toLowerCase()}`, whole, "team"); | |
| Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing | 116 | } else if (name) { |
| Merge branch 'worktree-agent-a8385d293d42c913a' | 117 | const href = name.toLowerCase() === "g1t" ? G1T_MENTION_HREF : `/${name.toLowerCase()}`; |
| 118 | replacement = link(href, whole, "mention"); | |
| Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing | 119 | } else if (hash && repo && /\d/.test(hash) && /[a-f]/i.test(hash)) { |
| 120 | replacement = link(`/${repo.namespace}/${repo.name}/commit/${hash}`, hash.slice(0, 7), "commit"); | |
| 121 | } | |
| 122 | if (!replacement) continue; | |
| 123 | if (match.index! > last) parts.push({ type: "text", value: value.slice(last, match.index) }); | |
| 124 | parts.push(replacement); | |
| 125 | last = match.index! + whole.length; | |
| 126 | } | |
| 127 | if (parts.length === 0) continue; | |
| 128 | if (last < value.length) parts.push({ type: "text", value: value.slice(last) }); | |
| 129 | children.splice(index, 1, ...parts); | |
| 130 | index += parts.length - 1; | |
| 131 | } | |
| 132 | }; | |
| 133 | walk(tree); | |
| 134 | }; | |
| 135 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.