CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
build-agent-spec.tsx
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 4a80519 | 1 | /** |
| 2 | * /docs/build-agent-integration — public spec page for AI build agents | |
| 3 | * integrating with Gluecron (Holden Mercer, Cursor, Claude Code, etc.). | |
| 4 | * | |
| 5 | * Renders a solid-white copyable spec block (high contrast against the | |
| 6 | * dark theme) telling the integrating vendor exactly what to change on | |
| 7 | * their side: detect Gluecron, swap base URL, swap auth token prefix, | |
| 8 | * call our REST v2 endpoints. Mirrors GitHub REST v3 by design so most | |
| 9 | * existing code reuses with a base-URL swap. | |
| 10 | * | |
| 11 | * Scoped CSS under `.ba-spec-*`. Public — no auth required. | |
| 12 | */ | |
| 13 | ||
| 14 | import { Hono } from "hono"; | |
| 15 | import { Layout } from "../views/layout"; | |
| 16 | import { softAuth } from "../middleware/auth"; | |
| 17 | import type { AuthEnv } from "../middleware/auth"; | |
| 18 | ||
| 19 | const buildAgentSpec = new Hono<AuthEnv>(); | |
| 20 | buildAgentSpec.use("*", softAuth); | |
| 21 | ||
| 22 | const styles = ` | |
| eed4684 | 23 | .ba-spec-wrap { max-width: 1320px; margin: 0 auto; padding: var(--space-6) var(--space-4); } |
| 4a80519 | 24 | |
| 25 | .ba-spec-hero { | |
| 26 | position: relative; | |
| 27 | margin-bottom: var(--space-5); | |
| 28 | padding: var(--space-5) var(--space-6); | |
| 29 | background: var(--bg-elevated); | |
| 30 | border: 1px solid var(--border); | |
| 31 | border-radius: 16px; | |
| 32 | overflow: hidden; | |
| 33 | } | |
| 34 | .ba-spec-hero::before { | |
| 35 | content: ''; | |
| 36 | position: absolute; | |
| 37 | top: 0; left: 0; right: 0; | |
| 38 | height: 2px; | |
| 6fd5915 | 39 | background: linear-gradient(90deg, transparent 0%, #5b6ee8 30%, #5f8fa0 70%, transparent 100%); |
| 4a80519 | 40 | opacity: 0.7; |
| 41 | pointer-events: none; | |
| 42 | } | |
| 43 | .ba-spec-hero-orb { | |
| 44 | position: absolute; | |
| 45 | inset: -20% -10% auto auto; | |
| 46 | width: 420px; height: 420px; | |
| 6fd5915 | 47 | background: radial-gradient(circle, rgba(91,110,232,0.20), rgba(95,143,160,0.10) 45%, transparent 70%); |
| 4a80519 | 48 | filter: blur(80px); |
| 49 | opacity: 0.7; | |
| 50 | pointer-events: none; | |
| 51 | z-index: 0; | |
| 52 | } | |
| 53 | .ba-spec-hero-inner { position: relative; z-index: 1; max-width: 760px; } | |
| 54 | .ba-spec-eyebrow { | |
| 55 | font-size: 12px; | |
| 56 | color: var(--text-muted); | |
| 57 | margin-bottom: var(--space-2); | |
| 58 | letter-spacing: 0.02em; | |
| 59 | display: inline-flex; | |
| 60 | align-items: center; | |
| 61 | gap: 8px; | |
| 62 | } | |
| 63 | .ba-spec-eyebrow .pill { | |
| 64 | display: inline-flex; | |
| 65 | align-items: center; | |
| 66 | justify-content: center; | |
| 67 | width: 18px; height: 18px; | |
| 68 | border-radius: 6px; | |
| 6fd5915 | 69 | background: rgba(91,110,232,0.14); |
| 70 | color: #5b6ee8; | |
| 71 | box-shadow: inset 0 0 0 1px rgba(91,110,232,0.35); | |
| 4a80519 | 72 | } |
| 73 | .ba-spec-title { | |
| 74 | font-size: clamp(28px, 4vw, 44px); | |
| 75 | font-family: var(--font-display); | |
| 76 | font-weight: 800; | |
| 77 | letter-spacing: -0.028em; | |
| 78 | line-height: 1.05; | |
| 79 | margin: 0 0 var(--space-2); | |
| 80 | color: var(--text-strong); | |
| 81 | } | |
| 82 | .ba-spec-title-grad { | |
| 6fd5915 | 83 | background-image: linear-gradient(135deg, #5b6ee8 0%, #5b6ee8 50%, #5f8fa0 100%); |
| 4a80519 | 84 | -webkit-background-clip: text; |
| 85 | background-clip: text; | |
| 86 | -webkit-text-fill-color: transparent; | |
| 87 | color: transparent; | |
| 88 | } | |
| 89 | .ba-spec-sub { | |
| 90 | font-size: 15px; | |
| 91 | color: var(--text-muted); | |
| 92 | margin: 0; | |
| 93 | line-height: 1.5; | |
| 94 | max-width: 680px; | |
| 95 | } | |
| 96 | ||
| 97 | /* Solid white spec block — high contrast against dark theme. */ | |
| 98 | .ba-spec-block { | |
| 99 | margin-bottom: var(--space-5); | |
| 100 | background: #ffffff; | |
| 101 | color: #0a0a0a; | |
| 102 | border: 1px solid #e5e7eb; | |
| 103 | border-radius: 14px; | |
| 104 | overflow: hidden; | |
| 105 | box-shadow: 0 1px 0 rgba(255,255,255,0.04), 0 8px 32px rgba(0,0,0,0.18); | |
| 106 | } | |
| 107 | .ba-spec-head { | |
| 108 | display: flex; | |
| 109 | align-items: center; | |
| 110 | justify-content: space-between; | |
| 111 | gap: 12px; | |
| 112 | padding: 12px 16px; | |
| 113 | background: #f9fafb; | |
| 114 | border-bottom: 1px solid #e5e7eb; | |
| 115 | flex-wrap: wrap; | |
| 116 | } | |
| 117 | .ba-spec-title-bar { | |
| 118 | display: flex; | |
| 119 | align-items: center; | |
| 120 | gap: 10px; | |
| 121 | font-family: var(--font-display, system-ui, sans-serif); | |
| 122 | font-size: 14px; | |
| 123 | font-weight: 700; | |
| 124 | color: #111827; | |
| 125 | letter-spacing: -0.005em; | |
| 126 | margin: 0; | |
| 127 | } | |
| 128 | .ba-spec-dot { | |
| 129 | width: 8px; height: 8px; | |
| 130 | border-radius: 9999px; | |
| 6fd5915 | 131 | background: linear-gradient(135deg, #5b6ee8, #5f8fa0); |
| 132 | box-shadow: 0 0 0 3px rgba(91,110,232,0.18); | |
| 4a80519 | 133 | } |
| 134 | .ba-spec-copy { | |
| 135 | display: inline-flex; | |
| 136 | align-items: center; | |
| 137 | gap: 6px; | |
| 138 | padding: 6px 12px; | |
| 139 | font-size: 12.5px; | |
| 140 | font-weight: 600; | |
| 141 | color: #111827; | |
| 142 | background: #ffffff; | |
| 143 | border: 1px solid #d1d5db; | |
| 144 | border-radius: 8px; | |
| 145 | cursor: pointer; | |
| 146 | font-family: inherit; | |
| 147 | transition: background 120ms ease, border-color 120ms ease, color 120ms ease; | |
| 148 | } | |
| 149 | .ba-spec-copy:hover { | |
| 150 | background: #f3f4f6; | |
| 151 | border-color: #9ca3af; | |
| 152 | } | |
| 153 | .ba-spec-copy.is-copied { | |
| 154 | background: #ecfdf5; | |
| 155 | border-color: #6ee7b7; | |
| 156 | color: #047857; | |
| 157 | } | |
| 158 | .ba-spec-copy svg { display: block; } | |
| 159 | .ba-spec-pre { | |
| 160 | margin: 0; | |
| 161 | padding: 22px 24px; | |
| 162 | font-family: var(--font-mono, ui-monospace, "SF Mono", Menlo, monospace); | |
| 163 | font-size: 12.5px; | |
| 164 | line-height: 1.7; | |
| 165 | color: #0a0a0a; | |
| 166 | background: #ffffff; | |
| 167 | white-space: pre; | |
| 168 | overflow-x: auto; | |
| 169 | tab-size: 2; | |
| 170 | max-height: 70vh; | |
| 171 | overflow-y: auto; | |
| 172 | } | |
| 173 | .ba-spec-foot { | |
| 174 | padding: 10px 16px; | |
| 175 | border-top: 1px solid #e5e7eb; | |
| 176 | background: #f9fafb; | |
| 177 | font-size: 12px; | |
| 178 | color: #6b7280; | |
| 179 | } | |
| 180 | .ba-spec-foot code { | |
| 181 | background: #eef2ff; | |
| 182 | color: #4338ca; | |
| 183 | padding: 1px 6px; | |
| 184 | border-radius: 4px; | |
| 185 | font-size: 11.5px; | |
| 186 | } | |
| 187 | ||
| 188 | .ba-spec-callout { | |
| 189 | margin-top: var(--space-5); | |
| 190 | padding: var(--space-4); | |
| 191 | text-align: center; | |
| 192 | color: var(--text-muted); | |
| 193 | font-size: 13px; | |
| 194 | border: 1px dashed var(--border); | |
| 195 | border-radius: 12px; | |
| 196 | } | |
| 197 | .ba-spec-callout a { | |
| 198 | color: var(--accent); | |
| 199 | text-decoration: none; | |
| 200 | font-weight: 600; | |
| 201 | } | |
| 202 | .ba-spec-callout a:hover { text-decoration: underline; } | |
| 203 | `; | |
| 204 | ||
| 205 | const SPEC_TEXT = `═══════════════════════════════════════════════════════════════════════════════ | |
| 206 | HOLDEN MERCER × GLUECRON INTEGRATION SPEC | |
| 207 | What HM needs to change on its side to support Gluecron-hosted repos | |
| 208 | Gluecron API v1.1 — all endpoints below are LIVE (rolled out 2026-05-20) | |
| 209 | ═══════════════════════════════════════════════════════════════════════════════ | |
| 210 | ||
| 211 | TL;DR | |
| 212 | ───────────────────────────────────────────────────────────────────────────── | |
| 213 | Your earlier read of Gluecron's API was based on v1, which lacked | |
| 214 | /contents, /git/*, and /actions/workflows/:filename/dispatches. Those | |
| 215 | gaps are now closed. Shapes match GitHub REST v3 exactly — a base-URL | |
| 216 | swap reuses most of your existing GitHub integration. The "clone + | |
| 217 | push" workaround you architected still works (path A below); the | |
| 218 | native dispatch flow (path B) is now also available if you want it. | |
| 219 | ||
| 220 | Required changes on HM's side | |
| 221 | ───────────────────────────────────────────────────────────────────────────── | |
| 222 | ||
| 223 | 1. ADD GLUECRON AS A DESTINATION | |
| 224 | Surface Gluecron alongside GitHub in your repo picker. Detect with: | |
| 225 | ||
| 226 | hostname === "gluecron.com" | |
| 227 | || process.env.GLUECRON_HOST // self-hosted instances | |
| 228 | ||
| 229 | 2. SWAP THE BASE URL | |
| 230 | GitHub: https://api.github.com | |
| 231 | Gluecron: https://gluecron.com/api/v2 | |
| 232 | (or https://<GLUECRON_HOST>/api/v2 for self-hosted) | |
| 233 | ||
| 234 | 3. SWAP THE AUTH TOKEN PREFIX — header name stays | |
| 235 | GitHub: Authorization: Bearer ghp_… | |
| 236 | Gluecron: Authorization: Bearer glc_… (PAT, scope=repo) | |
| 237 | Or OAuth: Bearer glct_… (admin scope NOT issuable) | |
| 238 | Or App install:Bearer ghi_… | |
| 239 | ||
| 240 | 4. CHANGE NOTHING ELSE IN YOUR REQUEST/RESPONSE PARSING | |
| 241 | - Same field names (snake_case) | |
| 242 | - Same status codes | |
| 243 | - Same base64 content encoding | |
| 244 | - Same git-objects API surface | |
| 245 | ||
| 246 | Endpoints HM will call | |
| 247 | ───────────────────────────────────────────────────────────────────────────── | |
| 248 | ||
| 249 | READ FILES | |
| 250 | GET /api/v2/repos/:owner/:repo/contents/:path?ref=<branch_or_sha> | |
| 251 | → { type, name, path, size, sha, encoding:"base64", content, | |
| 252 | html_url, download_url } | |
| 253 | ||
| 254 | ATOMIC MULTI-FILE WRITE (preferred for agent turns) | |
| 255 | POST /api/v2/repos/:owner/:repo/git/blobs | |
| 256 | body: { content, encoding: "utf-8" | "base64" } | |
| 257 | → { sha, url, size } | |
| 258 | ||
| 259 | POST /api/v2/repos/:owner/:repo/git/trees | |
| 260 | body: { base_tree?, tree: [{ path, mode:"100644", | |
| 261 | type:"blob", sha|null }] } | |
| 262 | → { sha, url, tree[] } | |
| 263 | (sha:null in a tree entry = delete that path from base_tree) | |
| 264 | ||
| 265 | POST /api/v2/repos/:owner/:repo/git/commits | |
| 266 | body: { message, tree, parents: [<head_sha>] } | |
| 267 | → { sha, tree, message, parents, author, html_url } | |
| 268 | ||
| 269 | GET /api/v2/repos/:owner/:repo/git/refs/heads/:branch | |
| 270 | → { ref, object: { sha, type:"commit" } } | |
| 271 | ||
| 272 | PATCH /api/v2/repos/:owner/:repo/git/refs/heads/:branch | |
| 273 | body: { sha: <new_commit>, force?: false } | |
| 274 | → { ref, object } (422 if not fast-forward and force=false) | |
| 275 | ||
| 276 | OPEN A PULL REQUEST | |
| 277 | POST /api/v2/repos/:owner/:repo/pulls | |
| 278 | body: { title, body, base, head } | |
| 279 | → { id, number, title, state, html_url, ... } | |
| 280 | ||
| 281 | DISPATCH A NATIVE BUILD ON GLUECRON ACTIONS (optional — path B) | |
| 282 | POST /api/v2/repos/:owner/:repo/actions/workflows/:filename/dispatches | |
| 283 | body: { ref:"main", inputs?: { prompt, model, … } } | |
| 284 | → 204 No Content | |
| 285 | ||
| 286 | GET /api/v2/repos/:owner/:repo/actions/workflows/:filename/runs | |
| 287 | ?branch=&head_sha=&per_page=30&page=1 | |
| 288 | → { total_count, workflow_runs:[...] } | |
| 289 | ||
| 290 | GET /api/v2/repos/:owner/:repo/actions/runs/:run_id | |
| 291 | → { id, name, head_branch, head_sha, status, conclusion, | |
| 292 | event, created_at, updated_at, run_started_at, html_url } | |
| 293 | ||
| 294 | GET /api/v2/repos/:owner/:repo/actions/runs/:run_id/logs | |
| 295 | → application/zip (one .log file per job) | |
| 296 | ||
| 297 | POST /api/v2/repos/:owner/:repo/actions/runs/:run_id/cancel | |
| 298 | → 202 Accepted (409 if already terminal) | |
| 299 | ||
| 300 | User-side workflow file (one-time setup per repo, for path B) | |
| 301 | ───────────────────────────────────────────────────────────────────────────── | |
| 302 | ||
| 303 | If HM dispatches via path B, the user's repo needs a YAML committed to | |
| 304 | .gluecron/workflows/holden-mercer.yml. Recommend this template: | |
| 305 | ||
| 306 | name: Holden Mercer build agent | |
| 307 | on: | |
| 308 | workflow_dispatch: | |
| 309 | inputs: | |
| 310 | prompt: | |
| 311 | description: What the agent should build / fix | |
| 312 | required: true | |
| 313 | type: string | |
| 314 | model: | |
| 315 | description: Anthropic model to use | |
| 316 | required: false | |
| 317 | default: claude-haiku-4-5 | |
| 318 | type: string | |
| 319 | jobs: | |
| 320 | agent: | |
| 321 | runs-on: ubuntu-latest | |
| 322 | steps: | |
| 323 | - uses: actions/checkout@v4 | |
| 324 | - name: Run HM agent | |
| 325 | env: | |
| 326 | ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }} | |
| 327 | HM_PROMPT: \${{ inputs.prompt }} | |
| 328 | HM_MODEL: \${{ inputs.model }} | |
| 329 | run: npx @holden-mercer/agent run | |
| 330 | ||
| 331 | Two integration paths — pick one (or offer both in HM's UI) | |
| 332 | ───────────────────────────────────────────────────────────────────────────── | |
| 333 | ||
| 334 | PATH A — Your-backend mode (matches your original v1 workaround) | |
| 335 | 1. Clone the repo using a credential helper (NOT URL-embedded token): | |
| 336 | git -c credential.helper=… clone https://gluecron.com/:owner/:repo.git | |
| 337 | 2. Run the tool-use loop on HM's backend against local files | |
| 338 | 3. Push the result via the git-objects API (one atomic commit): | |
| 339 | POST /git/blobs → /git/trees → /git/commits → PATCH /git/refs/heads | |
| 340 | 4. POST /pulls to open the PR | |
| 341 | Pros: full control, you own compute. Cons: you bear all build minutes. | |
| 342 | ||
| 343 | PATH B — Gluecron-native mode (uses our runner) | |
| 344 | 1. POST /actions/workflows/holden-mercer.yml/dispatches with inputs | |
| 345 | 2. Your agent code runs inside Gluecron Actions on the user's repo | |
| 346 | 3. The runner pushes commits + opens the PR itself | |
| 347 | Pros: zero infra on your side, lower egress. Cons: Gluecron concurrency | |
| 348 | + timeout limits apply per tier. | |
| 349 | ||
| 350 | Both paths supported. Most vendors ship A first, add B later as an opt-in. | |
| 351 | ||
| 352 | Webhook callback (optional but recommended) | |
| 353 | ───────────────────────────────────────────────────────────────────────────── | |
| 354 | Subscribe to pull_request + workflow_run events at | |
| 355 | /:owner/:repo/settings/webhooks | |
| 356 | HMAC signature header: X-Gluecron-Signature: sha256=<hex> | |
| 357 | Delivery: at-least-once with exponential backoff | |
| 358 | (30s, 2m, 10m, 1h, 6h — max 6 attempts then | |
| 359 | dead-letter) | |
| 360 | Dedupe with: X-Gluecron-Delivery: <uuid> | |
| 361 | ||
| 362 | Compatibility checklist (matches GitHub REST v3 by design) | |
| 363 | ───────────────────────────────────────────────────────────────────────────── | |
| 364 | [x] Same endpoint shapes | |
| 365 | [x] Same field names (snake_case) | |
| 366 | [x] Same base64 content encoding | |
| 367 | [x] Same status codes | |
| 368 | [x] Same git plumbing API (blobs / trees / commits / refs) | |
| 369 | [x] Same workflow_dispatch shape | |
| 370 | [x] HMAC-SHA256 webhook signatures | |
| 371 | [x] PKCE OAuth (RFC 7636) | |
| 372 | [x] Pagination via ?limit, ?offset (default 30, max 100) | |
| 373 | ||
| 374 | Differences to flag in HM's client code | |
| 375 | ───────────────────────────────────────────────────────────────────────────── | |
| 376 | - Token prefix: glc_ (PAT) / glct_ (OAuth) / ghi_ (app install) | |
| 377 | - Pagination: ?limit not ?per_page (recommend limit) | |
| 378 | - OAuth scopes: namespaced — read:repo / write:repo (admin NOT issuable) | |
| 379 | - Webhook header: X-Gluecron-Signature not X-Hub-Signature-256 | |
| 380 | ||
| 381 | Estimated work on HM's side | |
| 382 | ───────────────────────────────────────────────────────────────────────────── | |
| 383 | - UI change (add Gluecron destination) ~2-4 hrs | |
| 384 | - API client base-URL/token swap ~2-3 hrs | |
| 385 | - Webhook handler for X-Gluecron-Signature ~1 hr | |
| 386 | - Path B dispatch integration (optional) ~4-6 hrs | |
| 387 | TOTAL minimum (paths A only): ~5-8 hrs | |
| 388 | TOTAL with path B as well: ~10-15 hrs | |
| 389 | ||
| 390 | Support | |
| 391 | ───────────────────────────────────────────────────────────────────────────── | |
| 392 | Live spec: https://gluecron.com/docs/build-agent-integration (this page) | |
| 393 | Status: https://gluecron.com/admin/health | |
| 394 | Issues: https://gluecron.com/ccantynz/Gluecron.com/issues | |
| 395 | Contact: ccantynz on gluecron.com | |
| 396 | ═══════════════════════════════════════════════════════════════════════════════`; | |
| 397 | ||
| 0feb720 | 398 | const MCP_TOOLS_TEXT = `═══════════════════════════════════════════════════════════════════════════════ |
| 399 | GLUECRON MCP TOOL SURFACE (50+ tools, model-context-protocol/2025-06-18) | |
| 400 | ═══════════════════════════════════════════════════════════════════════════════ | |
| 401 | ||
| 402 | Transport: POST /mcp — JSON-RPC 2.0 (single or batch) | |
| 403 | GET /mcp — discovery handshake | |
| 404 | Auth: same as REST v2 — Bearer glc_… (PAT) or session cookie | |
| 405 | agent tokens (agt_…) routed via agent-multiplayer | |
| 406 | ||
| 407 | Call shape: | |
| 408 | { | |
| 409 | "jsonrpc": "2.0", "id": 1, | |
| 410 | "method": "tools/call", | |
| 411 | "params": { "name": "<tool_name>", "arguments": { … } } | |
| 412 | } | |
| 413 | ||
| 414 | ─── READ TOOLS (public, no auth) ──────────────────────────────────────────── | |
| 415 | gluecron_repo_search Search public repos by keyword | |
| 416 | gluecron_repo_read_file Read a file at a ref | |
| 417 | gluecron_repo_list_issues List open issues on a repo | |
| 418 | gluecron_repo_explain_codebase Cached AI 'explain this codebase' markdown | |
| 419 | gluecron_repo_health Health score + breakdown for a repo | |
| 420 | ||
| 421 | ─── REPOS (write — requires 'repo' or 'admin') ────────────────────────────── | |
| 422 | gluecron_fork_repo Fork a repo to the caller's namespace | |
| 423 | gluecron_delete_repo Permanently delete a repo (admin) | |
| 424 | gluecron_update_repo Description / visibility / default branch | |
| 425 | gluecron_search_repos Full search (sort, limit, filters) | |
| 426 | gluecron_clone_url Authed HTTPS clone URL + helper hint | |
| 427 | ||
| 428 | ─── ISSUES (write — requires 'repo') ──────────────────────────────────────── | |
| 429 | gluecron_create_issue Open a new issue | |
| 430 | gluecron_comment_issue Add a comment | |
| 431 | gluecron_close_issue Close an open issue (idempotent) | |
| 432 | gluecron_reopen_issue Reopen a closed issue | |
| 433 | gluecron_label_issue Attach labels (auto-creates missing) | |
| 434 | gluecron_unlabel_issue Detach a single label | |
| 435 | gluecron_assign_issue Assign to a user (via assignee:* label) | |
| 436 | gluecron_search_issues Title/body keyword search per repo | |
| 437 | ||
| 438 | ─── PULL REQUESTS (write — requires 'repo') ───────────────────────────────── | |
| 439 | gluecron_create_pr Open a PR | |
| 440 | gluecron_open_draft_pr Open a draft PR | |
| 441 | gluecron_get_pr Fetch full PR record | |
| 442 | gluecron_list_prs List PRs by state | |
| 443 | gluecron_search_prs Keyword search on title/body | |
| 444 | gluecron_comment_pr Add a PR comment | |
| 445 | gluecron_request_changes Post an AI-review 'changes requested' comment | |
| 446 | gluecron_merge_pr Merge a PR (enforces every gate + risk score) | |
| 447 | gluecron_close_pr Close without merging (idempotent) | |
| 448 | gluecron_generate_pr_description AI commit-message-style description | |
| 449 | ||
| 450 | ─── FILES & GIT PLUMBING (write — requires 'repo') ────────────────────────── | |
| 451 | gluecron_read_file GET /contents wrapper | |
| 452 | gluecron_write_file PUT /contents wrapper (utf8 or base64) | |
| 453 | gluecron_delete_file DELETE /contents wrapper | |
| 454 | gluecron_list_tree GET /tree wrapper (recursive supported) | |
| 455 | gluecron_get_commit GET /commits/:sha wrapper | |
| 456 | gluecron_create_branch Create a new branch ref at a sha | |
| 457 | gluecron_atomic_multi_file_commit blob/tree/commit/ref-update in one call | |
| 458 | — the killer agent tool | |
| 459 | ||
| 460 | ─── AI WORKFLOWS (Gluecron-native — requires 'repo') ──────────────────────── | |
| 461 | gluecron_ship_spec Drop .gluecron/specs/*.md status:ready | |
| 462 | gluecron_voice_to_pr Transcript → spec or issue (auto-classified) | |
| 463 | gluecron_refactor_across_repos Plan + execute a multi-repo refactor | |
| 464 | gluecron_explain_repo Cached 'explain this repo' markdown | |
| 465 | gluecron_chat_with_repo Start a chat + send the first message | |
| 466 | gluecron_chat_continue Send another message in a chat | |
| 467 | gluecron_generate_tests AI tests for a PR (follow-up-pr / append) | |
| 468 | gluecron_generate_commit_message Conventional / plain commit message for a diff | |
| 469 | gluecron_generate_release_notes Section-bucketed notes between two tags | |
| 470 | gluecron_propose_migration Dep-upgrade PR via migration-assistant | |
| 471 | gluecron_propose_doc_update Doc-drift scan + PR opens | |
| 472 | ||
| 473 | ─── CI / DEPLOYS (write — requires 'repo') ────────────────────────────────── | |
| 474 | gluecron_trigger_workflow workflow_dispatch | |
| 475 | gluecron_get_workflow_run Run status + metadata | |
| 476 | gluecron_get_workflow_logs Per-job log payload (JSON; ZIP via REST) | |
| 477 | gluecron_cancel_workflow_run Cancel queued/running run | |
| 478 | gluecron_get_preview_url Branch-preview URL + status | |
| 479 | gluecron_provision_pr_sandbox Re-provision a PR sandbox | |
| 480 | ||
| 481 | ─── AGENTS (multiplayer surface — admin to mint, repo to lease) ───────────── | |
| 482 | gluecron_create_agent_session Mint agent token (returned ONCE) (admin) | |
| 483 | gluecron_acquire_lease Grab an exclusive target lease | |
| 484 | gluecron_release_lease Release a held lease | |
| 485 | gluecron_get_agent_budget spent / cap / remaining cents | |
| 486 | ||
| 487 | ─── SEMANTIC ──────────────────────────────────────────────────────────────── | |
| 488 | gluecron_semantic_search Vector-index query (Voyage or hash) | |
| 489 | gluecron_find_symbol findDefinitions wrapper | |
| 490 | ||
| 491 | ─── INSIGHTS ──────────────────────────────────────────────────────────────── | |
| 492 | gluecron_pr_status_summary State + risk + trio verdict for a PR | |
| 493 | gluecron_repo_health (also under READ; included here for parity) | |
| 494 | gluecron_ai_cost_summary Spend rollup (user / repo / agent) | |
| 495 | ||
| 496 | ═══════════════════════════════════════════════════════════════════════════════`; | |
| 497 | ||
| 4a80519 | 498 | buildAgentSpec.get("/docs/build-agent-integration", (c) => { |
| 499 | const user = c.get("user"); | |
| 500 | return c.html( | |
| 501 | <Layout title="Build agent integration spec — Gluecron" user={user}> | |
| 502 | <div class="ba-spec-wrap"> | |
| 503 | <section class="ba-spec-hero"> | |
| 504 | <div class="ba-spec-hero-orb" aria-hidden="true" /> | |
| 505 | <div class="ba-spec-hero-inner"> | |
| 506 | <div class="ba-spec-eyebrow"> | |
| 507 | <span class="pill" aria-hidden="true"> | |
| 508 | <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"> | |
| 509 | <polyline points="16 18 22 12 16 6" /> | |
| 510 | <polyline points="8 6 2 12 8 18" /> | |
| 511 | </svg> | |
| 512 | </span> | |
| 513 | Integration spec · For AI build-agent vendors (Holden Mercer, Cursor, Claude Code) | |
| 514 | </div> | |
| 515 | <h1 class="ba-spec-title"> | |
| 516 | <span class="ba-spec-title-grad">Wire your agent into Gluecron.</span> | |
| 517 | </h1> | |
| 518 | <p class="ba-spec-sub"> | |
| 519 | Drop-in compatible with GitHub REST v3. One base-URL swap reuses your existing | |
| 520 | GitHub integration. Copy the block below and paste it to your engineering team — | |
| 521 | everything they need to ship is in there. | |
| 522 | </p> | |
| 523 | </div> | |
| 524 | </section> | |
| 525 | ||
| 526 | <section class="ba-spec-block" aria-labelledby="ba-spec-title"> | |
| 527 | <header class="ba-spec-head"> | |
| 528 | <div> | |
| 529 | <p class="ba-spec-title-bar" id="ba-spec-title"> | |
| 530 | <span class="ba-spec-dot" aria-hidden="true" /> | |
| 531 | HM × Gluecron integration spec | |
| 532 | </p> | |
| 533 | </div> | |
| 534 | <button | |
| 535 | type="button" | |
| 536 | class="ba-spec-copy" | |
| 537 | data-spec-copy | |
| 538 | aria-label="Copy spec to clipboard" | |
| 539 | > | |
| 540 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> | |
| 541 | <rect x="9" y="9" width="13" height="13" rx="2" ry="2" /> | |
| 542 | <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" /> | |
| 543 | </svg> | |
| 544 | <span data-spec-copy-label>Copy spec</span> | |
| 545 | </button> | |
| 546 | </header> | |
| 547 | <pre class="ba-spec-pre" data-spec-text>{SPEC_TEXT}</pre> | |
| 548 | <div class="ba-spec-foot"> | |
| 549 | Share this page directly: <code>https://gluecron.com/docs/build-agent-integration</code> | |
| 550 | </div> | |
| 551 | </section> | |
| 552 | ||
| 0feb720 | 553 | {/* ─── MCP tools surface ─────────────────────────────────────── */} |
| 554 | <section class="ba-spec-block" aria-labelledby="ba-mcp-title"> | |
| 555 | <header class="ba-spec-head"> | |
| 556 | <div> | |
| 557 | <p class="ba-spec-title-bar" id="ba-mcp-title"> | |
| 558 | <span class="ba-spec-dot" aria-hidden="true" /> | |
| 559 | MCP tools — every Gluecron action callable from any AI agent | |
| 560 | </p> | |
| 561 | </div> | |
| 562 | </header> | |
| 563 | <pre class="ba-spec-pre">{MCP_TOOLS_TEXT}</pre> | |
| 564 | <div class="ba-spec-foot"> | |
| 565 | JSON-RPC 2.0 endpoint: <code>POST https://gluecron.com/mcp</code> — | |
| 566 | send <code>{"{ method: \"tools/list\" }"}</code> to enumerate at runtime. | |
| 567 | </div> | |
| 568 | </section> | |
| 569 | ||
| 4a80519 | 570 | <div class="ba-spec-callout"> |
| 571 | Looking for the full Gluecron platform spec? See{" "} | |
| 572 | <a href="/help">/help</a> and{" "} | |
| 573 | <a href="/admin/integrations">/admin/integrations</a> for env-var setup. | |
| 574 | </div> | |
| 575 | </div> | |
| 576 | <style dangerouslySetInnerHTML={{ __html: styles }} /> | |
| 577 | <script | |
| 578 | dangerouslySetInnerHTML={{ | |
| 579 | __html: ` | |
| 580 | (function(){ | |
| 581 | var btn = document.querySelector('[data-spec-copy]'); | |
| 582 | var pre = document.querySelector('[data-spec-text]'); | |
| 583 | var label = document.querySelector('[data-spec-copy-label]'); | |
| 584 | if (!btn || !pre || !label) return; | |
| 585 | btn.addEventListener('click', function(){ | |
| 586 | var text = pre.textContent || ''; | |
| 587 | var done = function(){ | |
| 588 | btn.classList.add('is-copied'); | |
| 589 | label.textContent = 'Copied'; | |
| 590 | setTimeout(function(){ | |
| 591 | btn.classList.remove('is-copied'); | |
| 592 | label.textContent = 'Copy spec'; | |
| 593 | }, 1800); | |
| 594 | }; | |
| 595 | if (navigator.clipboard && navigator.clipboard.writeText) { | |
| 596 | navigator.clipboard.writeText(text).then(done).catch(function(){ | |
| 597 | var ta = document.createElement('textarea'); | |
| 598 | ta.value = text; ta.style.position='fixed'; ta.style.left='-9999px'; | |
| 599 | document.body.appendChild(ta); ta.select(); | |
| 600 | try { document.execCommand('copy'); done(); } catch(e){} | |
| 601 | document.body.removeChild(ta); | |
| 602 | }); | |
| 603 | } else { | |
| 604 | var ta = document.createElement('textarea'); | |
| 605 | ta.value = text; ta.style.position='fixed'; ta.style.left='-9999px'; | |
| 606 | document.body.appendChild(ta); ta.select(); | |
| 607 | try { document.execCommand('copy'); done(); } catch(e){} | |
| 608 | document.body.removeChild(ta); | |
| 609 | } | |
| 610 | }); | |
| 611 | })(); | |
| 612 | `, | |
| 613 | }} | |
| 614 | /> | |
| 615 | </Layout> | |
| 616 | ); | |
| 617 | }); | |
| 618 | ||
| 619 | export default buildAgentSpec; |