CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
docs.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.
| e0e4219 | 1 | /** |
| 2 | * /docs — expanded documentation site. | |
| 3 | * | |
| 4 | * Routes: | |
| 5 | * GET /docs — landing page with links to all sections | |
| 6 | * GET /docs/getting-started — push your first repo in 60 seconds | |
| 7 | * GET /docs/workflow-yaml — workflow YAML syntax reference | |
| 8 | * GET /docs/mcp-server — MCP server setup + tool catalogue | |
| 9 | * GET /docs/api — REST API reference | |
| 10 | * GET /docs/agents — agent publishing guide | |
| 11 | * | |
| 12 | * Uses softAuth so the nav bar renders with the signed-in user's session | |
| 13 | * cookie when present; the pages are reachable without auth. | |
| 14 | */ | |
| 15 | ||
| 16 | import { Hono } from "hono"; | |
| 17 | import { Layout } from "../views/layout"; | |
| 18 | import { softAuth } from "../middleware/auth"; | |
| 19 | import type { AuthEnv } from "../middleware/auth"; | |
| 20 | ||
| 21 | const docs = new Hono<AuthEnv>(); | |
| 22 | docs.use("*", softAuth); | |
| 23 | ||
| 24 | // ─── SHARED STYLES ────────────────────────────────────────────────────────── | |
| 25 | const docsStyles = ` | |
| 26 | /* ─── Layout ─── */ | |
| 27 | .docs-wrap { | |
| 28 | max-width: 1320px; | |
| 29 | margin: 0 auto; | |
| 30 | padding: var(--space-6) var(--space-4); | |
| 31 | display: grid; | |
| 32 | grid-template-columns: 240px 1fr; | |
| 33 | gap: var(--space-6); | |
| 34 | align-items: start; | |
| 35 | } | |
| 36 | @media (max-width: 800px) { | |
| 37 | .docs-wrap { grid-template-columns: 1fr; } | |
| 38 | .docs-sidebar { display: none; } | |
| 39 | } | |
| 40 | ||
| 41 | /* ─── Sidebar ─── */ | |
| 42 | .docs-sidebar { | |
| 43 | position: sticky; | |
| 44 | top: calc(var(--header-h, 56px) + var(--space-4)); | |
| 45 | background: var(--bg-elevated); | |
| 46 | border: 1px solid var(--border); | |
| 47 | border-radius: 14px; | |
| 48 | padding: var(--space-4); | |
| 49 | } | |
| 50 | .docs-sidebar-label { | |
| 51 | font-size: 11px; | |
| 52 | font-weight: 700; | |
| 53 | letter-spacing: 0.08em; | |
| 54 | text-transform: uppercase; | |
| 55 | color: var(--accent); | |
| 56 | margin-bottom: var(--space-3); | |
| 57 | padding-bottom: 8px; | |
| 58 | border-bottom: 1px solid var(--border); | |
| 59 | } | |
| 60 | .docs-sidebar-nav { | |
| 61 | display: flex; | |
| 62 | flex-direction: column; | |
| 63 | gap: 2px; | |
| 64 | } | |
| 65 | .docs-sidebar-nav a { | |
| 66 | display: block; | |
| 67 | padding: 7px 10px; | |
| 68 | font-size: 13.5px; | |
| 69 | color: var(--text); | |
| 70 | text-decoration: none; | |
| 71 | border-radius: 8px; | |
| 72 | transition: background 120ms ease, color 120ms ease; | |
| 73 | } | |
| 74 | .docs-sidebar-nav a:hover { | |
| 6fd5915 | 75 | background: rgba(91,110,232,0.08); |
| e0e4219 | 76 | color: var(--text-strong); |
| 77 | } | |
| 78 | .docs-sidebar-nav a.docs-nav-active { | |
| 6fd5915 | 79 | background: rgba(91,110,232,0.14); |
| e0e4219 | 80 | color: #c4b6ff; |
| 81 | font-weight: 600; | |
| 82 | } | |
| 83 | ||
| 84 | /* ─── Hero (landing page only) ─── */ | |
| 85 | .docs-hero { | |
| 86 | position: relative; | |
| 87 | margin-bottom: var(--space-5); | |
| 88 | padding: var(--space-5) var(--space-6); | |
| 89 | background: var(--bg-elevated); | |
| 90 | border: 1px solid var(--border); | |
| 91 | border-radius: 16px; | |
| 92 | overflow: hidden; | |
| 93 | } | |
| 94 | .docs-hero::before { | |
| 95 | content: ''; | |
| 96 | position: absolute; | |
| 97 | top: 0; left: 0; right: 0; | |
| 98 | height: 2px; | |
| 6fd5915 | 99 | background: linear-gradient(90deg, transparent 0%, #5b6ee8 30%, #5f8fa0 70%, transparent 100%); |
| e0e4219 | 100 | opacity: 0.75; |
| 101 | } | |
| 102 | .docs-hero-orb { | |
| 103 | position: absolute; | |
| 104 | inset: -20% -10% auto auto; | |
| 105 | width: 320px; height: 320px; | |
| 6fd5915 | 106 | background: radial-gradient(circle, rgba(91,110,232,0.18), rgba(95,143,160,0.09) 45%, transparent 70%); |
| e0e4219 | 107 | filter: blur(80px); |
| 108 | opacity: 0.7; | |
| 109 | pointer-events: none; | |
| 110 | } | |
| 111 | .docs-hero-inner { position: relative; z-index: 1; max-width: 640px; } | |
| 112 | .docs-hero-eyebrow { | |
| 113 | font-size: 11px; | |
| 114 | font-weight: 700; | |
| 115 | letter-spacing: 0.08em; | |
| 116 | text-transform: uppercase; | |
| 117 | color: var(--accent); | |
| 118 | margin-bottom: var(--space-2); | |
| 119 | } | |
| 120 | .docs-hero-title { | |
| 121 | font-size: clamp(26px, 3.5vw, 38px); | |
| 122 | font-family: var(--font-display); | |
| 123 | font-weight: 800; | |
| 124 | letter-spacing: -0.025em; | |
| 125 | margin: 0 0 var(--space-3); | |
| 126 | color: var(--text-strong); | |
| 127 | } | |
| 128 | .docs-hero-title .docs-gradient { | |
| 6fd5915 | 129 | background-image: linear-gradient(135deg, #5b6ee8 0%, #5b6ee8 50%, #5f8fa0 100%); |
| e0e4219 | 130 | -webkit-background-clip: text; |
| 131 | background-clip: text; | |
| 132 | -webkit-text-fill-color: transparent; | |
| 133 | color: transparent; | |
| 134 | } | |
| 135 | .docs-hero-sub { | |
| 136 | font-size: 15px; | |
| 137 | color: var(--text-muted); | |
| 138 | margin: 0; | |
| 139 | line-height: 1.55; | |
| 140 | } | |
| 141 | ||
| 142 | /* ─── Card grid (landing) ─── */ | |
| 143 | .docs-card-grid { | |
| 144 | display: grid; | |
| 145 | grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); | |
| 146 | gap: var(--space-4); | |
| 147 | margin-bottom: var(--space-5); | |
| 148 | } | |
| 149 | .docs-card { | |
| 150 | background: var(--bg-elevated); | |
| 151 | border: 1px solid var(--border); | |
| 152 | border-radius: 14px; | |
| 153 | padding: var(--space-4) var(--space-5); | |
| 154 | text-decoration: none; | |
| 155 | transition: border-color 150ms ease, box-shadow 150ms ease; | |
| 156 | display: block; | |
| 157 | } | |
| 158 | .docs-card:hover { | |
| 6fd5915 | 159 | border-color: rgba(91,110,232,0.45); |
| 160 | box-shadow: 0 4px 24px -8px rgba(91,110,232,0.25); | |
| e0e4219 | 161 | } |
| 162 | .docs-card-icon { | |
| 163 | font-size: 24px; | |
| 164 | margin-bottom: var(--space-2); | |
| 165 | } | |
| 166 | .docs-card-title { | |
| 167 | font-size: 15px; | |
| 168 | font-weight: 700; | |
| 169 | color: var(--text-strong); | |
| 170 | margin: 0 0 6px; | |
| 171 | } | |
| 172 | .docs-card-desc { | |
| 173 | font-size: 13px; | |
| 174 | color: var(--text-muted); | |
| 175 | line-height: 1.5; | |
| 176 | margin: 0; | |
| 177 | } | |
| 178 | ||
| 179 | /* ─── Main content area ─── */ | |
| 180 | .docs-content h1 { | |
| 181 | font-family: var(--font-display); | |
| 182 | font-size: clamp(22px, 3vw, 30px); | |
| 183 | font-weight: 800; | |
| 184 | letter-spacing: -0.022em; | |
| 185 | color: var(--text-strong); | |
| 186 | margin: 0 0 var(--space-2); | |
| 187 | } | |
| 188 | .docs-content .docs-page-sub { | |
| 189 | font-size: 14.5px; | |
| 190 | color: var(--text-muted); | |
| 191 | margin: 0 0 var(--space-5); | |
| 192 | line-height: 1.55; | |
| 193 | } | |
| 194 | .docs-content h2 { | |
| 195 | font-family: var(--font-display); | |
| 196 | font-size: 18px; | |
| 197 | font-weight: 700; | |
| 198 | letter-spacing: -0.015em; | |
| 199 | color: var(--text-strong); | |
| 200 | margin: var(--space-6) 0 var(--space-3); | |
| 201 | padding-top: var(--space-4); | |
| 202 | border-top: 1px solid var(--border); | |
| 203 | scroll-margin-top: calc(var(--header-h, 56px) + var(--space-3)); | |
| 204 | } | |
| 205 | .docs-content h2:first-of-type { border-top: none; margin-top: 0; padding-top: 0; } | |
| 206 | .docs-content h3 { | |
| 207 | font-size: 15px; | |
| 208 | font-weight: 700; | |
| 209 | color: var(--text-strong); | |
| 210 | margin: var(--space-4) 0 var(--space-2); | |
| 211 | } | |
| 212 | .docs-content p { | |
| 213 | font-size: 14px; | |
| 214 | line-height: 1.65; | |
| 215 | color: var(--text); | |
| 216 | margin: 0 0 var(--space-3); | |
| 217 | } | |
| 218 | .docs-content ul, .docs-content ol { | |
| 219 | font-size: 14px; | |
| 220 | line-height: 1.65; | |
| 221 | color: var(--text); | |
| 222 | margin: 0 0 var(--space-3); | |
| 223 | padding-left: var(--space-4); | |
| 224 | } | |
| 225 | .docs-content li { margin-bottom: 6px; } | |
| 226 | .docs-content code { | |
| 227 | font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); | |
| 228 | font-size: 12.5px; | |
| 229 | padding: 1px 5px; | |
| 230 | border-radius: 4px; | |
| 6fd5915 | 231 | background: rgba(91,110,232,0.10); |
| e0e4219 | 232 | color: #c4b6ff; |
| 233 | } | |
| 234 | .docs-content pre { | |
| 235 | margin: 0 0 var(--space-3); | |
| 236 | padding: 16px 18px; | |
| 237 | background: #0a0c14; | |
| 238 | border: 1px solid var(--border); | |
| 239 | border-radius: 10px; | |
| 240 | font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); | |
| 241 | font-size: 13px; | |
| 242 | line-height: 1.6; | |
| 243 | color: #d6d8e3; | |
| 244 | overflow-x: auto; | |
| 245 | } | |
| 246 | .docs-content pre code { | |
| 247 | background: none; | |
| 248 | padding: 0; | |
| 249 | color: inherit; | |
| 250 | font-size: inherit; | |
| 251 | } | |
| 252 | .docs-content a { color: var(--accent); } | |
| 253 | .docs-content table { | |
| 254 | width: 100%; | |
| 255 | border-collapse: collapse; | |
| 256 | font-size: 13.5px; | |
| 257 | margin: 0 0 var(--space-4); | |
| 258 | } | |
| 259 | .docs-content th { | |
| 260 | text-align: left; | |
| 261 | padding: 8px 12px; | |
| 262 | background: var(--bg-elevated); | |
| 263 | border: 1px solid var(--border); | |
| 264 | font-weight: 600; | |
| 265 | color: var(--text-strong); | |
| 266 | } | |
| 267 | .docs-content td { | |
| 268 | padding: 8px 12px; | |
| 269 | border: 1px solid var(--border); | |
| 270 | color: var(--text); | |
| 271 | vertical-align: top; | |
| 272 | } | |
| 273 | .docs-content td code { font-size: 12px; } | |
| 274 | ||
| 275 | /* ─── Page nav (prev/next) ─── */ | |
| 276 | .docs-page-nav { | |
| 277 | display: flex; | |
| 278 | justify-content: space-between; | |
| 279 | gap: var(--space-4); | |
| 280 | margin-top: var(--space-6); | |
| 281 | padding-top: var(--space-4); | |
| 282 | border-top: 1px solid var(--border); | |
| 283 | } | |
| 284 | .docs-page-nav-item { | |
| 285 | display: flex; | |
| 286 | flex-direction: column; | |
| 287 | gap: 4px; | |
| 288 | text-decoration: none; | |
| 289 | padding: 12px 16px; | |
| 290 | background: var(--bg-elevated); | |
| 291 | border: 1px solid var(--border); | |
| 292 | border-radius: 10px; | |
| 293 | transition: border-color 150ms ease; | |
| 294 | max-width: 220px; | |
| 295 | flex: 1; | |
| 296 | } | |
| 6fd5915 | 297 | .docs-page-nav-item:hover { border-color: rgba(91,110,232,0.45); } |
| e0e4219 | 298 | .docs-page-nav-item--next { text-align: right; margin-left: auto; } |
| 299 | .docs-page-nav-label { | |
| 300 | font-size: 11px; | |
| 301 | font-weight: 600; | |
| 302 | letter-spacing: 0.07em; | |
| 303 | text-transform: uppercase; | |
| 304 | color: var(--text-muted); | |
| 305 | } | |
| 306 | .docs-page-nav-title { | |
| 307 | font-size: 13.5px; | |
| 308 | font-weight: 600; | |
| 309 | color: var(--text-strong); | |
| 310 | } | |
| 311 | ||
| 312 | /* ─── Edit link ─── */ | |
| 313 | .docs-edit-link { | |
| 314 | display: inline-flex; | |
| 315 | align-items: center; | |
| 316 | gap: 6px; | |
| 317 | font-size: 12px; | |
| 318 | color: var(--text-muted); | |
| 319 | text-decoration: none; | |
| 320 | margin-top: var(--space-5); | |
| 321 | padding: 5px 10px; | |
| 322 | border: 1px solid var(--border); | |
| 323 | border-radius: 8px; | |
| 324 | transition: color 120ms ease, border-color 120ms ease; | |
| 325 | } | |
| 326 | .docs-edit-link:hover { | |
| 327 | color: var(--text); | |
| 6fd5915 | 328 | border-color: rgba(91,110,232,0.35); |
| e0e4219 | 329 | } |
| 330 | `; | |
| 331 | ||
| 332 | // ─── SIDEBAR NAV (shared) ───────────────────────────────────────────────── | |
| 333 | type DocSection = { href: string; label: string }; | |
| 334 | ||
| 335 | const NAV_SECTIONS: DocSection[] = [ | |
| 336 | { href: "/docs", label: "Overview" }, | |
| 337 | { href: "/docs/getting-started", label: "Getting started" }, | |
| 338 | { href: "/docs/workflow-yaml", label: "Workflow YAML" }, | |
| 339 | { href: "/docs/mcp-server", label: "MCP server" }, | |
| 340 | { href: "/docs/api", label: "API reference" }, | |
| 341 | { href: "/docs/agents", label: "Agent publishing" }, | |
| 342 | ]; | |
| 343 | ||
| 344 | function Sidebar({ current }: { current: string }) { | |
| 345 | return ( | |
| 346 | <aside class="docs-sidebar"> | |
| 347 | <div class="docs-sidebar-label">Documentation</div> | |
| 348 | <nav class="docs-sidebar-nav" aria-label="Docs sections"> | |
| 349 | {NAV_SECTIONS.map((s) => ( | |
| 350 | <a | |
| 351 | href={s.href} | |
| 352 | class={s.href === current ? "docs-nav-active" : ""} | |
| 353 | aria-current={s.href === current ? "page" : undefined} | |
| 354 | > | |
| 355 | {s.label} | |
| 356 | </a> | |
| 357 | ))} | |
| 358 | </nav> | |
| 359 | </aside> | |
| 360 | ); | |
| 361 | } | |
| 362 | ||
| 363 | function PageNav({ | |
| 364 | prev, | |
| 365 | next, | |
| 366 | }: { | |
| 367 | prev?: DocSection; | |
| 368 | next?: DocSection; | |
| 369 | }) { | |
| 370 | return ( | |
| 371 | <div class="docs-page-nav"> | |
| 372 | {prev ? ( | |
| 373 | <a href={prev.href} class="docs-page-nav-item"> | |
| 374 | <span class="docs-page-nav-label">← Previous</span> | |
| 375 | <span class="docs-page-nav-title">{prev.label}</span> | |
| 376 | </a> | |
| 377 | ) : ( | |
| 378 | <span /> | |
| 379 | )} | |
| 380 | {next ? ( | |
| 381 | <a href={next.href} class="docs-page-nav-item docs-page-nav-item--next"> | |
| 382 | <span class="docs-page-nav-label">Next →</span> | |
| 383 | <span class="docs-page-nav-title">{next.label}</span> | |
| 384 | </a> | |
| 385 | ) : null} | |
| 386 | </div> | |
| 387 | ); | |
| 388 | } | |
| 389 | ||
| 390 | function EditLink({ path }: { path: string }) { | |
| 391 | return ( | |
| 392 | <a | |
| 393 | href={`/ccantynz/Gluecron.com/blob/main/${path}`} | |
| 394 | class="docs-edit-link" | |
| 395 | title="Edit this page in the source repo" | |
| 396 | > | |
| 397 | <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> | |
| 398 | <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" /> | |
| 399 | <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" /> | |
| 400 | </svg> | |
| 401 | Edit this page | |
| 402 | </a> | |
| 403 | ); | |
| 404 | } | |
| 405 | ||
| 406 | // ─── LANDING (/docs) ────────────────────────────────────────────────────── | |
| 407 | ||
| 408 | docs.get("/docs", (c) => { | |
| 409 | const user = c.get("user"); | |
| 410 | return c.html( | |
| 411 | <Layout title="Docs — gluecron" user={user} description="Gluecron documentation — getting started, workflow YAML, MCP server, REST API, and agent publishing."> | |
| 412 | <style dangerouslySetInnerHTML={{ __html: docsStyles }} /> | |
| 413 | <div class="docs-wrap"> | |
| 414 | <Sidebar current="/docs" /> | |
| 415 | <div class="docs-content"> | |
| 416 | {/* Hero */} | |
| 417 | <div class="docs-hero"> | |
| 418 | <div class="docs-hero-orb" aria-hidden="true" /> | |
| 419 | <div class="docs-hero-inner"> | |
| 420 | <div class="docs-hero-eyebrow">Documentation</div> | |
| 421 | <h1 class="docs-hero-title"> | |
| 422 | Build on <span class="docs-gradient">gluecron</span> | |
| 423 | </h1> | |
| 424 | <p class="docs-hero-sub"> | |
| 425 | Everything you need to host code, automate CI, talk to your | |
| 426 | repos from Claude, and publish agents to the marketplace. | |
| 427 | </p> | |
| 428 | </div> | |
| 429 | </div> | |
| 430 | ||
| 431 | {/* Card grid */} | |
| 432 | <div class="docs-card-grid"> | |
| 433 | <a href="/docs/getting-started" class="docs-card"> | |
| 434 | <div class="docs-card-icon">▶</div> | |
| 435 | <div class="docs-card-title">Getting started</div> | |
| 436 | <p class="docs-card-desc"> | |
| 437 | Create an account, create a repo, and push your first commit — | |
| 438 | in under 60 seconds. | |
| 439 | </p> | |
| 440 | </a> | |
| 441 | <a href="/docs/workflow-yaml" class="docs-card"> | |
| 442 | <div class="docs-card-icon">⚙</div> | |
| 443 | <div class="docs-card-title">Workflow YAML</div> | |
| 444 | <p class="docs-card-desc"> | |
| 445 | Triggers, steps, env vars, secrets, matrix builds, and the | |
| 446 | cache action — full syntax reference. | |
| 447 | </p> | |
| 448 | </a> | |
| 449 | <a href="/docs/mcp-server" class="docs-card"> | |
| 450 | <div class="docs-card-icon">🔗</div> | |
| 451 | <div class="docs-card-title">MCP server</div> | |
| 452 | <p class="docs-card-desc"> | |
| 453 | Add the Gluecron MCP config to Claude Code and get 15+ tools | |
| 454 | for driving repos from AI sessions. | |
| 455 | </p> | |
| 456 | </a> | |
| 457 | <a href="/docs/api" class="docs-card"> | |
| 458 | <div class="docs-card-icon">📄</div> | |
| 459 | <div class="docs-card-title">API reference</div> | |
| 460 | <p class="docs-card-desc"> | |
| 461 | Bearer-token auth, repos, issues, PRs, webhooks, and rate | |
| 462 | limits. Core endpoints with request/response shapes. | |
| 463 | </p> | |
| 464 | </a> | |
| 465 | <a href="/docs/agents" class="docs-card"> | |
| 466 | <div class="docs-card-icon">🤖</div> | |
| 467 | <div class="docs-card-title">Agent publishing</div> | |
| 468 | <p class="docs-card-desc"> | |
| 469 | Write an agent, test it locally, publish to the marketplace, | |
| 470 | and earn 70% of every sale. | |
| 471 | </p> | |
| 472 | </a> | |
| 473 | </div> | |
| 474 | ||
| 475 | <EditLink path="src/routes/docs.tsx" /> | |
| 476 | </div> | |
| 477 | </div> | |
| 478 | </Layout> | |
| 479 | ); | |
| 480 | }); | |
| 481 | ||
| 482 | // ─── GETTING STARTED (/docs/getting-started) ───────────────────────────── | |
| 483 | ||
| 484 | docs.get("/docs/getting-started", (c) => { | |
| 485 | const user = c.get("user"); | |
| 486 | const idx = NAV_SECTIONS.findIndex((s) => s.href === "/docs/getting-started"); | |
| 487 | const prev = NAV_SECTIONS[idx - 1]; | |
| 488 | const next = NAV_SECTIONS[idx + 1]; | |
| 489 | ||
| 490 | return c.html( | |
| 491 | <Layout title="Getting started — Docs — gluecron" user={user} description="Push your first repo to Gluecron in under 60 seconds."> | |
| 492 | <style dangerouslySetInnerHTML={{ __html: docsStyles }} /> | |
| 493 | <div class="docs-wrap"> | |
| 494 | <Sidebar current="/docs/getting-started" /> | |
| 495 | <div class="docs-content"> | |
| 496 | <h1>Getting started</h1> | |
| 497 | <p class="docs-page-sub"> | |
| 498 | Push your first repo to Gluecron in under 60 seconds. No special | |
| 499 | tooling required — just git. | |
| 500 | </p> | |
| 501 | ||
| 502 | <h2 id="create-account">1. Create an account</h2> | |
| 503 | <p> | |
| 504 | Head to <a href="/register">/register</a>, pick a username and | |
| 505 | password. Your username becomes part of every repo URL: | |
| 506 | <code>gluecron.com/<username>/<repo></code>. | |
| 507 | </p> | |
| 508 | <p> | |
| 509 | Verify your email when the one-time link arrives. Verified | |
| 510 | addresses receive issue, PR, and gate-run notifications. | |
| 511 | </p> | |
| 512 | ||
| 513 | <h2 id="create-repo">2. Create your first repo</h2> | |
| 514 | <p> | |
| 515 | From the dashboard hit <strong>+ New</strong>, or visit{" "} | |
| 516 | <a href="/new">/new</a>. Choose: | |
| 517 | </p> | |
| 518 | <ul> | |
| 519 | <li> | |
| 520 | <strong>Name</strong> — lowercase, hyphens OK, no spaces. | |
| 521 | </li> | |
| 522 | <li> | |
| 523 | <strong>Visibility</strong> — public (anyone can clone) or | |
| 524 | private (only you and collaborators). | |
| 525 | </li> | |
| 526 | <li> | |
| 527 | <strong>Initialize with README</strong> — tick this so you get | |
| 528 | a default branch immediately. | |
| 529 | </li> | |
| 530 | </ul> | |
| 531 | ||
| 532 | <h2 id="push-existing">3. Push an existing local repo</h2> | |
| 533 | <p> | |
| 534 | Add gluecron as a remote and push your default branch. Replace | |
| 535 | <code>you</code> and <code>my-project</code> with your actual | |
| 536 | username and repo name. | |
| 537 | </p> | |
| 538 | <pre><code>{`# Add the remote | |
| 539 | git remote add origin https://gluecron.com/you/my-project.git | |
| 540 | ||
| 541 | # Push (first time — track the upstream branch) | |
| 542 | git push -u origin main`}</code></pre> | |
| 543 | <p> | |
| 544 | Git will prompt for your username and password. Use your | |
| 545 | Gluecron password, or better yet a{" "} | |
| 546 | <a href="/settings/tokens">personal access token</a> (tokens | |
| 547 | start with <code>glc_</code> and never expire until revoked). | |
| 548 | </p> | |
| 549 | ||
| 550 | <h2 id="clone">4. Clone an existing repo</h2> | |
| 551 | <pre><code>{`git clone https://gluecron.com/you/my-project.git | |
| 552 | cd my-project`}</code></pre> | |
| 553 | ||
| 554 | <h2 id="ssh">5. Switch to SSH (recommended)</h2> | |
| 555 | <p> | |
| 556 | HTTPS is fine for one-off clones. For daily use, SSH is smoother — | |
| 557 | no password prompts. | |
| 558 | </p> | |
| 559 | <ol> | |
| 560 | <li> | |
| 561 | Copy your public key:{" "} | |
| 562 | <code>cat ~/.ssh/id_ed25519.pub</code> | |
| 563 | </li> | |
| 564 | <li> | |
| 565 | Paste it at{" "} | |
| 566 | <a href="/settings/keys">/settings/keys</a> and save. | |
| 567 | </li> | |
| 568 | <li> | |
| 569 | Re-clone with the SSH URL: | |
| 570 | <pre><code>{`git clone git@gluecron.com:you/my-project.git`}</code></pre> | |
| 571 | </li> | |
| 572 | </ol> | |
| 573 | ||
| 574 | <h2 id="import">6. Import from GitHub</h2> | |
| 575 | <p> | |
| 576 | Already have repos on GitHub? Visit{" "} | |
| 577 | <a href="/import">/import</a>, paste the GitHub URL (public or | |
| 578 | private), and Gluecron mirrors the full history, branches, and tags | |
| 579 | in one shot. Subsequent pushes go to Gluecron directly. | |
| 580 | </p> | |
| 581 | ||
| 582 | <h2 id="next">What happens after push?</h2> | |
| 583 | <p> | |
| 584 | Every push to the default branch triggers the{" "} | |
| 585 | <code>post-receive</code> hook: | |
| 586 | </p> | |
| 587 | <ul> | |
| 588 | <li> | |
| 589 | <strong>GateTest scan</strong> — checks the diff for leaked | |
| 590 | secrets, dependency advisories, and policy violations. Results | |
| 591 | appear on the commit page within seconds. | |
| 592 | </li> | |
| 593 | <li> | |
| 594 | <strong>AI review</strong> — if the push targets an open PR, | |
| 595 | the AI reviewer comments on changed files automatically. | |
| 596 | </li> | |
| 597 | <li> | |
| 598 | <strong>Workflow runs</strong> — any{" "} | |
| 599 | <code>.gluecron/workflows/*.yml</code> files with a{" "} | |
| 600 | <code>push</code> trigger fire immediately. | |
| 601 | </li> | |
| 602 | <li> | |
| 603 | <strong>Webhooks</strong> — registered webhook URLs receive a{" "} | |
| 604 | <code>push</code> event payload within ~1 second. | |
| 605 | </li> | |
| 606 | </ul> | |
| 607 | ||
| 608 | <PageNav prev={prev} next={next} /> | |
| 609 | <EditLink path="src/routes/docs.tsx" /> | |
| 610 | </div> | |
| 611 | </div> | |
| 612 | </Layout> | |
| 613 | ); | |
| 614 | }); | |
| 615 | ||
| 616 | // ─── WORKFLOW YAML (/docs/workflow-yaml) ────────────────────────────────── | |
| 617 | ||
| 618 | docs.get("/docs/workflow-yaml", (c) => { | |
| 619 | const user = c.get("user"); | |
| 620 | const idx = NAV_SECTIONS.findIndex((s) => s.href === "/docs/workflow-yaml"); | |
| 621 | const prev = NAV_SECTIONS[idx - 1]; | |
| 622 | const next = NAV_SECTIONS[idx + 1]; | |
| 623 | ||
| 624 | return c.html( | |
| 625 | <Layout title="Workflow YAML — Docs — gluecron" user={user} description="Gluecron workflow YAML syntax reference — triggers, steps, env vars, secrets, matrix builds."> | |
| 626 | <style dangerouslySetInnerHTML={{ __html: docsStyles }} /> | |
| 627 | <div class="docs-wrap"> | |
| 628 | <Sidebar current="/docs/workflow-yaml" /> | |
| 629 | <div class="docs-content"> | |
| 630 | <h1>Workflow YAML syntax</h1> | |
| 631 | <p class="docs-page-sub"> | |
| 632 | Workflows live in <code>.gluecron/workflows/*.yml</code> at the | |
| 633 | repo root. Gluecron runs them on the same node that handles your | |
| 634 | pushes — no external scheduler required. | |
| 635 | </p> | |
| 636 | ||
| 637 | <h2 id="minimal">Minimal example</h2> | |
| 638 | <pre><code>{`name: CI | |
| 639 | ||
| 640 | on: | |
| 641 | push: | |
| 642 | branches: [main] | |
| 643 | ||
| 644 | jobs: | |
| 645 | test: | |
| 646 | runs-on: ubuntu-latest | |
| 647 | steps: | |
| 648 | - uses: actions/checkout@v4 | |
| 649 | - run: bun install --frozen-lockfile | |
| 650 | - run: bun test`}</code></pre> | |
| 651 | ||
| 652 | <h2 id="triggers">Triggers</h2> | |
| 653 | <p> | |
| 654 | The <code>on:</code> key controls when a workflow fires. Multiple | |
| 655 | triggers can be combined in a single file. | |
| 656 | </p> | |
| 657 | ||
| 658 | <h3 id="push-trigger">push</h3> | |
| 659 | <pre><code>{`on: | |
| 660 | push: | |
| 661 | branches: [main, "release/**"] | |
| 662 | # Optional — only run when these paths change: | |
| 663 | paths: | |
| 664 | - "src/**" | |
| 665 | - "package.json"`}</code></pre> | |
| 666 | ||
| 667 | <h3 id="pr-trigger">pull_request</h3> | |
| 668 | <pre><code>{`on: | |
| 669 | pull_request: | |
| 670 | types: [opened, synchronize, reopened] | |
| 671 | branches: [main]`}</code></pre> | |
| 672 | ||
| 673 | <h3 id="schedule-trigger">schedule (cron)</h3> | |
| 674 | <p> | |
| 675 | Drop a cron expression in the <code>schedule</code> array. The | |
| 676 | Gluecron autopilot ticker fires it from the same node — no | |
| 677 | external scheduler needed. | |
| 678 | </p> | |
| 679 | <pre><code>{`on: | |
| 680 | schedule: | |
| 681 | # Every day at 06:00 UTC | |
| 682 | - cron: "0 6 * * *" | |
| 683 | # Every Monday at 09:00 UTC | |
| 684 | - cron: "0 9 * * 1"`}</code></pre> | |
| 685 | ||
| 686 | <h3 id="manual-trigger">workflow_dispatch (manual)</h3> | |
| 687 | <pre><code>{`on: | |
| 688 | workflow_dispatch: | |
| 689 | inputs: | |
| 690 | environment: | |
| 691 | description: "Target environment" | |
| 692 | required: true | |
| 693 | default: staging | |
| 694 | type: choice | |
| 695 | options: [staging, production]`}</code></pre> | |
| 696 | ||
| 697 | <h2 id="jobs">Jobs</h2> | |
| 698 | <p> | |
| 699 | Each job runs in a fresh container. Jobs within the same workflow | |
| 700 | run in parallel by default; use <code>needs:</code> to sequence | |
| 701 | them. | |
| 702 | </p> | |
| 703 | <pre><code>{`jobs: | |
| 704 | build: | |
| 705 | runs-on: ubuntu-latest | |
| 706 | steps: | |
| 707 | - uses: actions/checkout@v4 | |
| 708 | - run: bun run build | |
| 709 | ||
| 710 | deploy: | |
| 711 | runs-on: ubuntu-latest | |
| 712 | needs: build # waits for build to succeed | |
| 713 | if: github.ref == 'refs/heads/main' | |
| 714 | steps: | |
| 715 | - run: echo "Deploying…"`}</code></pre> | |
| 716 | ||
| 717 | <h2 id="steps">Steps</h2> | |
| 718 | ||
| 719 | <h3>run — shell commands</h3> | |
| 720 | <pre><code>{`steps: | |
| 721 | - name: Install deps | |
| 722 | run: bun install --frozen-lockfile | |
| 723 | ||
| 724 | - name: Multi-line script | |
| 725 | run: | | |
| 726 | bun run lint | |
| 727 | bun run typecheck | |
| 728 | bun test --reporter=verbose`}</code></pre> | |
| 729 | ||
| 730 | <h3>uses — reusable actions</h3> | |
| 731 | <pre><code>{`steps: | |
| 732 | - uses: actions/checkout@v4 # check out the repo | |
| 733 | ||
| 734 | - uses: oven-sh/setup-bun@v2 # install Bun | |
| 735 | with: | |
| 736 | bun-version: latest | |
| 737 | ||
| 738 | - uses: actions/cache@v4 # cache restore/save | |
| 739 | with: | |
| 740 | path: ~/.bun/install/cache | |
| 741 | key: \${{ runner.os }}-bun-\${{ hashFiles('**/bun.lockb') }}`}</code></pre> | |
| 742 | ||
| 743 | <h2 id="env">Environment variables</h2> | |
| 744 | <p> | |
| 745 | Set env vars at the workflow, job, or step level. Step-level | |
| 746 | values override job-level which override workflow-level. | |
| 747 | </p> | |
| 748 | <pre><code>{`env: | |
| 749 | NODE_ENV: test | |
| 750 | LOG_LEVEL: debug | |
| 751 | ||
| 752 | jobs: | |
| 753 | test: | |
| 754 | runs-on: ubuntu-latest | |
| 755 | env: | |
| 756 | DATABASE_URL: postgresql://localhost/testdb | |
| 757 | steps: | |
| 758 | - run: bun test | |
| 759 | env: | |
| 760 | VERBOSE: "1" # step-level override`}</code></pre> | |
| 761 | ||
| 762 | <h2 id="secrets">Secrets</h2> | |
| 763 | <p> | |
| 764 | Secrets are stored encrypted at the repo or org level and injected | |
| 765 | at runtime. They never appear in logs — values are masked | |
| 766 | automatically. | |
| 767 | </p> | |
| 768 | <pre><code>{`steps: | |
| 769 | - name: Deploy | |
| 770 | run: curl -X POST \$DEPLOY_WEBHOOK | |
| 771 | env: | |
| 772 | DEPLOY_WEBHOOK: \${{ secrets.DEPLOY_WEBHOOK_URL }}`}</code></pre> | |
| 773 | <p> | |
| 774 | Manage repo secrets at{" "} | |
| 775 | <code>/:owner/:repo/settings/secrets</code>. Org-level secrets | |
| 776 | live at <code>/orgs/:slug/settings/secrets</code> and are shared | |
| 777 | across all repos in the org. | |
| 778 | </p> | |
| 779 | ||
| 780 | <h2 id="matrix">Matrix builds</h2> | |
| 781 | <p> | |
| 782 | Run the same job across multiple configurations in parallel. | |
| 783 | </p> | |
| 784 | <pre><code>{`jobs: | |
| 785 | test: | |
| 786 | runs-on: ubuntu-latest | |
| 787 | strategy: | |
| 788 | matrix: | |
| 789 | node: [18, 20, 22] | |
| 790 | os: [ubuntu-latest, macos-latest] | |
| 791 | fail-fast: false # don't cancel others on first failure | |
| 792 | steps: | |
| 793 | - uses: actions/checkout@v4 | |
| 794 | - run: node --version # uses matrix.node implicitly via setup-node | |
| 795 | - run: bun test`}</code></pre> | |
| 796 | ||
| 797 | <h2 id="cache">Cache action</h2> | |
| 798 | <pre><code>{`# Bun package cache — speeds up installs by ~80% on warm hits | |
| 799 | - uses: actions/cache@v4 | |
| 800 | id: bun-cache | |
| 801 | with: | |
| 802 | path: ~/.bun/install/cache | |
| 803 | key: \${{ runner.os }}-bun-\${{ hashFiles('**/bun.lockb') }} | |
| 804 | restore-keys: | | |
| 805 | \${{ runner.os }}-bun- | |
| 806 | ||
| 807 | - run: bun install --frozen-lockfile`}</code></pre> | |
| 808 | ||
| 809 | <h2 id="expressions">Expressions</h2> | |
| 810 | <pre><code>{`# Context access | |
| 811 | \${{ github.actor }} # username that triggered the run | |
| 812 | \${{ github.ref }} # refs/heads/main | |
| 813 | \${{ github.sha }} # full commit SHA | |
| 814 | \${{ runner.os }} # Linux | macOS | Windows | |
| 815 | \${{ job.status }} # success | failure | cancelled | |
| 816 | ||
| 817 | # Functions | |
| 818 | \${{ hashFiles('**/bun.lockb') }} | |
| 819 | \${{ contains(github.ref, 'release') }} | |
| 820 | \${{ startsWith(github.ref, 'refs/tags/') }}`}</code></pre> | |
| 821 | ||
| 822 | <h2 id="complete-example">Complete CI + deploy example</h2> | |
| 823 | <pre><code>{`# .gluecron/workflows/ci.yml | |
| 824 | name: CI / Deploy | |
| 825 | ||
| 826 | on: | |
| 827 | push: | |
| 828 | branches: [main] | |
| 829 | pull_request: | |
| 830 | branches: [main] | |
| 831 | ||
| 832 | jobs: | |
| 833 | ci: | |
| 834 | runs-on: ubuntu-latest | |
| 835 | steps: | |
| 836 | - uses: actions/checkout@v4 | |
| 837 | ||
| 838 | - uses: actions/cache@v4 | |
| 839 | with: | |
| 840 | path: ~/.bun/install/cache | |
| 841 | key: \${{ runner.os }}-bun-\${{ hashFiles('**/bun.lockb') }} | |
| 842 | ||
| 843 | - uses: oven-sh/setup-bun@v2 | |
| 844 | with: | |
| 845 | bun-version: latest | |
| 846 | ||
| 847 | - run: bun install --frozen-lockfile | |
| 848 | - run: bun run typecheck | |
| 849 | - run: bun test | |
| 850 | ||
| 851 | deploy: | |
| 852 | runs-on: ubuntu-latest | |
| 853 | needs: ci | |
| 854 | if: github.ref == 'refs/heads/main' | |
| 855 | steps: | |
| 856 | - uses: actions/checkout@v4 | |
| 857 | - run: bun run build | |
| 858 | - name: Deploy to Fly.io | |
| 859 | run: flyctl deploy --remote-only | |
| 860 | env: | |
| 861 | FLY_API_TOKEN: \${{ secrets.FLY_API_TOKEN }}`}</code></pre> | |
| 862 | ||
| 863 | <PageNav prev={prev} next={next} /> | |
| 864 | <EditLink path="src/routes/docs.tsx" /> | |
| 865 | </div> | |
| 866 | </div> | |
| 867 | </Layout> | |
| 868 | ); | |
| 869 | }); | |
| 870 | ||
| 871 | // ─── MCP SERVER (/docs/mcp-server) ─────────────────────────────────────── | |
| 872 | ||
| 873 | docs.get("/docs/mcp-server", (c) => { | |
| 874 | const user = c.get("user"); | |
| 875 | const idx = NAV_SECTIONS.findIndex((s) => s.href === "/docs/mcp-server"); | |
| 876 | const prev = NAV_SECTIONS[idx - 1]; | |
| 877 | const next = NAV_SECTIONS[idx + 1]; | |
| 878 | ||
| 879 | return c.html( | |
| 880 | <Layout title="MCP server — Docs — gluecron" user={user} description="Set up the Gluecron MCP server in Claude Code and drive your repos from AI sessions."> | |
| 881 | <style dangerouslySetInnerHTML={{ __html: docsStyles }} /> | |
| 882 | <div class="docs-wrap"> | |
| 883 | <Sidebar current="/docs/mcp-server" /> | |
| 884 | <div class="docs-content"> | |
| 885 | <h1>MCP server</h1> | |
| 886 | <p class="docs-page-sub"> | |
| 887 | Gluecron ships a Model Context Protocol (MCP) server at{" "} | |
| 888 | <code>POST /mcp</code>. Add it to Claude Code and you can create | |
| 889 | issues, open PRs, merge pull requests, and more — all from an AI | |
| 890 | session, with full gate enforcement on every write. | |
| 891 | </p> | |
| 892 | ||
| 893 | <h2 id="install">1. Install Claude Code</h2> | |
| 894 | <p> | |
| 895 | If you don't have Claude Code yet, install it from{" "} | |
| 896 | <a href="https://claude.ai/code" target="_blank" rel="noopener">claude.ai/code</a>{" "} | |
| 897 | or via npm: | |
| 898 | </p> | |
| 899 | <pre><code>{`npm install -g @anthropic-ai/claude-code`}</code></pre> | |
| 900 | ||
| 901 | <h2 id="generate-token">2. Generate a personal access token</h2> | |
| 902 | <p> | |
| 903 | MCP write tools require authentication. Create a token at{" "} | |
| 904 | <a href="/settings/tokens">/settings/tokens</a> — choose the{" "} | |
| 905 | <strong>admin</strong> scope so merge and close operations are | |
| 906 | allowed. The token is shown once; copy it immediately. Tokens | |
| 907 | start with <code>glc_</code>. | |
| 908 | </p> | |
| 909 | ||
| 910 | <h2 id="add-config">3. Add the MCP config</h2> | |
| 911 | <p> | |
| 912 | Add the Gluecron server to your Claude Code{" "} | |
| 913 | <code>.claude/settings.json</code> (or the global | |
| 914 | <code>~/.claude/settings.json</code>): | |
| 915 | </p> | |
| 916 | <pre><code>{`{ | |
| 917 | "mcpServers": { | |
| 918 | "gluecron": { | |
| 919 | "type": "http", | |
| 920 | "url": "https://gluecron.com/mcp", | |
| 921 | "headers": { | |
| 922 | "Authorization": "Bearer glc_your_token_here" | |
| 923 | } | |
| 924 | } | |
| 925 | } | |
| 926 | }`}</code></pre> | |
| 927 | <p> | |
| 928 | If you self-host Gluecron, replace <code>gluecron.com</code> with | |
| 929 | your instance URL. | |
| 930 | </p> | |
| 931 | <p> | |
| 932 | Alternatively, one-click setup is available at{" "} | |
| 933 | <a href="/connect/claude">/connect/claude</a> — it generates the | |
| 934 | JSON snippet with your token pre-filled. | |
| 935 | </p> | |
| 936 | ||
| 937 | <h2 id="verify">4. Verify the connection</h2> | |
| 938 | <p>Start Claude Code in your project directory:</p> | |
| 939 | <pre><code>{`claude | |
| 940 | ||
| 941 | # In the session, ask: | |
| 942 | # "List open PRs on my-org/my-repo" | |
| 943 | # Claude will call gluecron_list_prs and return the results.`}</code></pre> | |
| 944 | ||
| 945 | <h2 id="tools">Available tools</h2> | |
| 946 | <p> | |
| 947 | The MCP server exposes the following tools. Read-only tools work | |
| 948 | without authentication; write tools require a token with the | |
| 949 | appropriate scope. | |
| 950 | </p> | |
| 951 | ||
| 952 | <h3>Read-only tools</h3> | |
| 953 | <table> | |
| 954 | <thead> | |
| 955 | <tr> | |
| 956 | <th>Tool</th> | |
| 957 | <th>Description</th> | |
| 958 | </tr> | |
| 959 | </thead> | |
| 960 | <tbody> | |
| 961 | <tr> | |
| 962 | <td><code>gluecron_repo_search</code></td> | |
| 963 | <td>Search public repos by keyword. Returns up to 20 results.</td> | |
| 964 | </tr> | |
| 965 | <tr> | |
| 966 | <td><code>gluecron_repo_read_file</code></td> | |
| 967 | <td>Read a single file from a public repo at a given ref (branch / tag / commit).</td> | |
| 968 | </tr> | |
| 969 | <tr> | |
| 970 | <td><code>gluecron_repo_list_issues</code></td> | |
| 971 | <td>List open issues for a public repo. Returns up to 50 ordered by most-recent.</td> | |
| 972 | </tr> | |
| 973 | <tr> | |
| 974 | <td><code>gluecron_repo_explain_codebase</code></td> | |
| 975 | <td>Return the cached AI "explain this codebase" Markdown for a public repo.</td> | |
| 976 | </tr> | |
| 977 | <tr> | |
| 978 | <td><code>gluecron_repo_health</code></td> | |
| 979 | <td>Compute the health report for a public repo: overall score (0-100), letter grade, per-category breakdown, and actionable insights.</td> | |
| 980 | </tr> | |
| 981 | </tbody> | |
| 982 | </table> | |
| 983 | ||
| 984 | <h3>Write tools (require auth)</h3> | |
| 985 | <table> | |
| 986 | <thead> | |
| 987 | <tr> | |
| 988 | <th>Tool</th> | |
| 989 | <th>Description</th> | |
| 990 | </tr> | |
| 991 | </thead> | |
| 992 | <tbody> | |
| 993 | <tr> | |
| 994 | <td><code>gluecron_create_issue</code></td> | |
| 995 | <td>Create a new issue. Returns <code>{"{number, url}"}</code>.</td> | |
| 996 | </tr> | |
| 997 | <tr> | |
| 998 | <td><code>gluecron_comment_issue</code></td> | |
| 999 | <td>Add a comment to an existing issue. Returns <code>{"{commentId}"}</code>.</td> | |
| 1000 | </tr> | |
| 1001 | <tr> | |
| 1002 | <td><code>gluecron_close_issue</code></td> | |
| 1003 | <td>Close an open issue (idempotent). Returns <code>{"{state}"}</code>.</td> | |
| 1004 | </tr> | |
| 1005 | <tr> | |
| 1006 | <td><code>gluecron_reopen_issue</code></td> | |
| 1007 | <td>Reopen a closed issue (idempotent). Returns <code>{"{state}"}</code>.</td> | |
| 1008 | </tr> | |
| 1009 | <tr> | |
| 1010 | <td><code>gluecron_create_pr</code></td> | |
| 1011 | <td>Open a new pull request. Returns <code>{"{number, url}"}</code>.</td> | |
| 1012 | </tr> | |
| 1013 | <tr> | |
| 1014 | <td><code>gluecron_get_pr</code></td> | |
| 1015 | <td>Fetch full PR detail (title, body, state, branches, draft, author).</td> | |
| 1016 | </tr> | |
| 1017 | <tr> | |
| 1018 | <td><code>gluecron_list_prs</code></td> | |
| 1019 | <td>List PRs filtered by state (open|closed|merged|all). Returns up to 50 rows.</td> | |
| 1020 | </tr> | |
| 1021 | <tr> | |
| 1022 | <td><code>gluecron_comment_pr</code></td> | |
| 1023 | <td>Add a comment to a pull request. Returns <code>{"{commentId}"}</code>.</td> | |
| 1024 | </tr> | |
| 1025 | <tr> | |
| 1026 | <td><code>gluecron_merge_pr</code></td> | |
| 1027 | <td>Merge an open PR. Enforces gate checks, branch-protection rules, and the pre-merge risk score. Returns <code>{"{merged, sha?, reason?, riskScore?}"}</code>.</td> | |
| 1028 | </tr> | |
| 1029 | <tr> | |
| 1030 | <td><code>gluecron_close_pr</code></td> | |
| 1031 | <td>Close a PR without merging (idempotent). Returns <code>{"{state}"}</code>.</td> | |
| 1032 | </tr> | |
| 1033 | </tbody> | |
| 1034 | </table> | |
| 1035 | ||
| 1036 | <h2 id="example-session">Example Claude session</h2> | |
| 1037 | <pre><code>{`# Open an issue | |
| 1038 | User: "Open an issue on my-org/api titled 'Rate limiter not resetting'" | |
| 1039 | Claude: [calls gluecron_create_issue] | |
| 1040 | Created issue #42: /my-org/api/issues/42 | |
| 1041 | ||
| 1042 | # Create a PR from a feature branch | |
| 1043 | User: "Open a PR from feat/rate-limit-fix into main" | |
| 1044 | Claude: [calls gluecron_create_pr with head_branch=feat/rate-limit-fix] | |
| 1045 | Opened PR #17: /my-org/api/pulls/17 | |
| 1046 | ||
| 1047 | # Merge after review | |
| 1048 | User: "Merge PR #17 — I've reviewed it" | |
| 1049 | Claude: [calls gluecron_merge_pr with number=17] | |
| 1050 | Merged PR #17. SHA: a3f9c2d`}</code></pre> | |
| 1051 | ||
| 1052 | <h2 id="gate-enforcement">Gate enforcement on merge</h2> | |
| 1053 | <p> | |
| 1054 | <code>gluecron_merge_pr</code> enforces the same checks as the | |
| 1055 | web UI merge button: | |
| 1056 | </p> | |
| 1057 | <ul> | |
| 1058 | <li>PR must not be a draft.</li> | |
| 1059 | <li>Head branch ref must resolve.</li> | |
| 1060 | <li> | |
| 1061 | GateTest hard gates must pass (secrets scan, dependency | |
| 1062 | advisories, policy violations). | |
| 1063 | </li> | |
| 1064 | <li>Branch-protection rules must be satisfied.</li> | |
| 1065 | <li> | |
| 1066 | If the pre-merge risk score is <em>critical</em>, the tool | |
| 1067 | returns <code>merged: false</code> with a prompt to re-call | |
| 1068 | with <code>confirm_high_risk: true</code>. | |
| 1069 | </li> | |
| 1070 | </ul> | |
| 1071 | ||
| 1072 | <h2 id="dxt">One-click Claude Desktop extension</h2> | |
| 1073 | <p> | |
| 1074 | Download the <code>.dxt</code> extension at{" "} | |
| 1075 | <a href="/gluecron.dxt">/gluecron.dxt</a>. Open it in Claude | |
| 1076 | Desktop — it installs the MCP config automatically. No JSON | |
| 1077 | editing needed. | |
| 1078 | </p> | |
| 1079 | ||
| 1080 | <PageNav prev={prev} next={next} /> | |
| 1081 | <EditLink path="src/routes/docs.tsx" /> | |
| 1082 | </div> | |
| 1083 | </div> | |
| 1084 | </Layout> | |
| 1085 | ); | |
| 1086 | }); | |
| 1087 | ||
| 1088 | // ─── API REFERENCE (/docs/api) ──────────────────────────────────────────── | |
| 1089 | ||
| 1090 | docs.get("/docs/api", (c) => { | |
| 1091 | const user = c.get("user"); | |
| 1092 | const idx = NAV_SECTIONS.findIndex((s) => s.href === "/docs/api"); | |
| 1093 | const prev = NAV_SECTIONS[idx - 1]; | |
| 1094 | const next = NAV_SECTIONS[idx + 1]; | |
| 1095 | ||
| 1096 | return c.html( | |
| 1097 | <Layout title="API reference — Docs — gluecron" user={user} description="Gluecron REST API reference — authentication, repos, issues, PRs, webhooks, rate limits."> | |
| 1098 | <style dangerouslySetInnerHTML={{ __html: docsStyles }} /> | |
| 1099 | <div class="docs-wrap"> | |
| 1100 | <Sidebar current="/docs/api" /> | |
| 1101 | <div class="docs-content"> | |
| 1102 | <h1>API reference</h1> | |
| 1103 | <p class="docs-page-sub"> | |
| 1104 | Gluecron exposes a REST API at <code>/api/v2/</code>. All | |
| 1105 | endpoints return JSON. Authenticated endpoints require a{" "} | |
| 1106 | <code>Bearer</code> token in the <code>Authorization</code> header. | |
| 1107 | </p> | |
| 1108 | ||
| 1109 | <h2 id="auth">Authentication</h2> | |
| 1110 | <p> | |
| 1111 | Generate a personal access token at{" "} | |
| 1112 | <a href="/settings/tokens">/settings/tokens</a>. Tokens start | |
| 1113 | with <code>glc_</code> and are hashed (SHA-256) before storage — | |
| 1114 | the plaintext is shown exactly once. | |
| 1115 | </p> | |
| 1116 | <pre><code>{`curl -H "Authorization: Bearer glc_your_token_here" \\ | |
| 1117 | https://gluecron.com/api/v2/repos`}</code></pre> | |
| 1118 | <p> | |
| 1119 | Tokens can also authenticate <code>git</code> over HTTPS — | |
| 1120 | use the token as the password with any username. | |
| 1121 | </p> | |
| 1122 | ||
| 1123 | <h2 id="rate-limits">Rate limits</h2> | |
| 1124 | <table> | |
| 1125 | <thead> | |
| 1126 | <tr> | |
| 1127 | <th>Surface</th> | |
| 1128 | <th>Limit</th> | |
| 1129 | <th>Window</th> | |
| 1130 | </tr> | |
| 1131 | </thead> | |
| 1132 | <tbody> | |
| 1133 | <tr> | |
| 1134 | <td>API (anonymous)</td> | |
| 1135 | <td>1 000 requests</td> | |
| 1136 | <td>60 seconds</td> | |
| 1137 | </tr> | |
| 1138 | <tr> | |
| 1139 | <td>API (authenticated)</td> | |
| 1140 | <td>4 000 requests</td> | |
| 1141 | <td>60 seconds</td> | |
| 1142 | </tr> | |
| 1143 | <tr> | |
| 1144 | <td>AI commit message</td> | |
| 1145 | <td>60 requests</td> | |
| 1146 | <td>60 seconds per token</td> | |
| 1147 | </tr> | |
| 1148 | <tr> | |
| 1149 | <td>Login / register</td> | |
| 1150 | <td>10–20 requests</td> | |
| 1151 | <td>60 seconds per IP</td> | |
| 1152 | </tr> | |
| 1153 | </tbody> | |
| 1154 | </table> | |
| 1155 | <p> | |
| 1156 | When a limit is exceeded the server returns{" "} | |
| 1157 | <code>429 Too Many Requests</code> with a{" "} | |
| 1158 | <code>Retry-After</code> header. | |
| 1159 | </p> | |
| 1160 | ||
| 1161 | <h2 id="repos">Repositories</h2> | |
| 1162 | ||
| 1163 | <h3>List your repos</h3> | |
| 1164 | <pre><code>{`GET /api/v2/repos | |
| 1165 | ||
| 1166 | Response 200: | |
| 1167 | [ | |
| 1168 | { | |
| 1169 | "id": "...", | |
| 1170 | "name": "my-project", | |
| 1171 | "fullName": "you/my-project", | |
| 1172 | "description": "...", | |
| 1173 | "isPrivate": false, | |
| 1174 | "defaultBranch": "main", | |
| 1175 | "starCount": 12, | |
| 1176 | "forkCount": 3, | |
| 1177 | "issueCount": 5, | |
| 1178 | "createdAt": "2025-01-15T10:00:00Z", | |
| 1179 | "updatedAt": "2025-06-01T14:30:00Z" | |
| 1180 | } | |
| 1181 | ]`}</code></pre> | |
| 1182 | ||
| 1183 | <h3>Create a repo</h3> | |
| 1184 | <pre><code>{`POST /api/v2/repos | |
| 1185 | Content-Type: application/json | |
| 1186 | ||
| 1187 | { | |
| 1188 | "name": "my-project", | |
| 1189 | "description": "Optional description", | |
| 1190 | "isPrivate": false, | |
| 1191 | "initReadme": true, | |
| 1192 | "defaultBranch": "main" | |
| 1193 | } | |
| 1194 | ||
| 1195 | Response 201: | |
| 1196 | { "id": "...", "name": "my-project", "fullName": "you/my-project" }`}</code></pre> | |
| 1197 | ||
| 1198 | <h3>Get a repo</h3> | |
| 1199 | <pre><code>{`GET /api/v2/repos/:owner/:repo | |
| 1200 | ||
| 1201 | Response 200: | |
| 1202 | { /* same shape as list item */ }`}</code></pre> | |
| 1203 | ||
| 1204 | <h3>Delete a repo</h3> | |
| 1205 | <pre><code>{`DELETE /api/v2/repos/:owner/:repo | |
| 1206 | ||
| 1207 | Response 204 No Content`}</code></pre> | |
| 1208 | ||
| 1209 | <h2 id="issues">Issues</h2> | |
| 1210 | ||
| 1211 | <h3>List issues</h3> | |
| 1212 | <pre><code>{`GET /api/v2/repos/:owner/:repo/issues?state=open&limit=25 | |
| 1213 | ||
| 1214 | Response 200: | |
| 1215 | [ | |
| 1216 | { | |
| 1217 | "number": 42, | |
| 1218 | "title": "Bug in auth flow", | |
| 1219 | "body": "...", | |
| 1220 | "state": "open", | |
| 1221 | "author": "kit", | |
| 1222 | "createdAt": "2025-05-20T08:00:00Z" | |
| 1223 | } | |
| 1224 | ]`}</code></pre> | |
| 1225 | ||
| 1226 | <h3>Create an issue</h3> | |
| 1227 | <pre><code>{`POST /api/v2/repos/:owner/:repo/issues | |
| 1228 | Content-Type: application/json | |
| 1229 | ||
| 1230 | { | |
| 1231 | "title": "Bug in auth flow", | |
| 1232 | "body": "Steps to reproduce: ..." | |
| 1233 | } | |
| 1234 | ||
| 1235 | Response 201: | |
| 1236 | { "number": 42, "url": "/you/my-project/issues/42" }`}</code></pre> | |
| 1237 | ||
| 1238 | <h3>Close / reopen an issue</h3> | |
| 1239 | <pre><code>{`PATCH /api/v2/repos/:owner/:repo/issues/:number | |
| 1240 | Content-Type: application/json | |
| 1241 | ||
| 1242 | { "state": "closed" } // or "open" | |
| 1243 | ||
| 1244 | Response 200: | |
| 1245 | { "state": "closed" }`}</code></pre> | |
| 1246 | ||
| 1247 | <h2 id="pull-requests">Pull requests</h2> | |
| 1248 | ||
| 1249 | <h3>List PRs</h3> | |
| 1250 | <pre><code>{`GET /api/v2/repos/:owner/:repo/pulls?state=open | |
| 1251 | ||
| 1252 | Response 200: | |
| 1253 | [ | |
| 1254 | { | |
| 1255 | "number": 17, | |
| 1256 | "title": "Add rate limiter", | |
| 1257 | "state": "open", | |
| 1258 | "baseBranch": "main", | |
| 1259 | "headBranch": "feat/rate-limit", | |
| 1260 | "isDraft": false, | |
| 1261 | "author": "kit", | |
| 1262 | "createdAt": "2025-06-01T09:00:00Z" | |
| 1263 | } | |
| 1264 | ]`}</code></pre> | |
| 1265 | ||
| 1266 | <h3>Create a PR</h3> | |
| 1267 | <pre><code>{`POST /api/v2/repos/:owner/:repo/pulls | |
| 1268 | Content-Type: application/json | |
| 1269 | ||
| 1270 | { | |
| 1271 | "title": "Add rate limiter", | |
| 1272 | "body": "## Summary\n- Implements token-bucket rate limiter\n\n## Test plan\n- [ ] Run bun test", | |
| 1273 | "headBranch": "feat/rate-limit", | |
| 1274 | "baseBranch": "main" | |
| 1275 | } | |
| 1276 | ||
| 1277 | Response 201: | |
| 1278 | { "number": 17, "url": "/you/my-project/pulls/17" }`}</code></pre> | |
| 1279 | ||
| 1280 | <h3>Merge a PR</h3> | |
| 1281 | <pre><code>{`POST /api/v2/repos/:owner/:repo/pulls/:number/merge | |
| 1282 | ||
| 1283 | Response 200: | |
| 1284 | { "merged": true, "sha": "a3f9c2d..." } | |
| 1285 | ||
| 1286 | // If gate checks fail: | |
| 1287 | Response 422: | |
| 1288 | { "merged": false, "reason": "GateTest: leaked secret in src/config.ts" }`}</code></pre> | |
| 1289 | ||
| 1290 | <h2 id="webhooks">Webhooks</h2> | |
| 1291 | ||
| 1292 | <h3>Register a webhook</h3> | |
| 1293 | <pre><code>{`POST /api/v2/repos/:owner/:repo/webhooks | |
| 1294 | Content-Type: application/json | |
| 1295 | ||
| 1296 | { | |
| 1297 | "url": "https://example.com/hooks/gluecron", | |
| 1298 | "secret": "mysecret", | |
| 1299 | "events": ["push", "pull_request", "issues", "star"] | |
| 1300 | } | |
| 1301 | ||
| 1302 | Response 201: | |
| 1303 | { "id": "...", "url": "https://example.com/hooks/gluecron" }`}</code></pre> | |
| 1304 | ||
| 1305 | <h3>Webhook payload shape</h3> | |
| 1306 | <pre><code>{`// push event | |
| 1307 | { | |
| 1308 | "event": "push", | |
| 1309 | "repo": { "owner": "you", "name": "my-project" }, | |
| 1310 | "ref": "refs/heads/main", | |
| 1311 | "before": "<sha>", | |
| 1312 | "after": "<sha>", | |
| 1313 | "commits": [ | |
| 1314 | { | |
| 1315 | "id": "<sha>", | |
| 1316 | "message": "feat: add rate limiter", | |
| 1317 | "author": { "name": "Kit", "email": "kit@example.com" } | |
| 1318 | } | |
| 1319 | ], | |
| 1320 | "sender": { "username": "kit" } | |
| 1321 | } | |
| 1322 | ||
| 1323 | // Signature header (verify with HMAC-SHA256 over raw body): | |
| 1324 | X-Gluecron-Signature: sha256=<hex>`}</code></pre> | |
| 1325 | ||
| 1326 | <h2 id="ai-endpoints">AI endpoints</h2> | |
| 1327 | ||
| 1328 | <h3>Generate a commit message</h3> | |
| 1329 | <pre><code>{`POST /api/v2/ai/commit-message | |
| 1330 | Content-Type: application/json | |
| 1331 | ||
| 1332 | { "diff": "<git diff output>" } | |
| 1333 | ||
| 1334 | Response 200: | |
| 1335 | { | |
| 1336 | "message": "feat(auth): add token-bucket rate limiter\\n\\nPrevents brute-force login attempts..." | |
| 1337 | }`}</code></pre> | |
| 1338 | ||
| 1339 | <h3>Semantic code search</h3> | |
| 1340 | <pre><code>{`GET /api/v2/repos/:owner/:repo/semantic-search?q=password+hashing&limit=10 | |
| 1341 | ||
| 1342 | Response 200: | |
| 1343 | [ | |
| 1344 | { | |
| 1345 | "path": "src/lib/auth.ts", | |
| 1346 | "startLine": 12, | |
| 1347 | "endLine": 28, | |
| 1348 | "snippet": "...", | |
| 1349 | "score": 0.94 | |
| 1350 | } | |
| 1351 | ]`}</code></pre> | |
| 1352 | ||
| 1353 | <PageNav prev={prev} next={next} /> | |
| 1354 | <EditLink path="src/routes/docs.tsx" /> | |
| 1355 | </div> | |
| 1356 | </div> | |
| 1357 | </Layout> | |
| 1358 | ); | |
| 1359 | }); | |
| 1360 | ||
| 1361 | // ─── AGENT PUBLISHING (/docs/agents) ───────────────────────────────────── | |
| 1362 | ||
| 1363 | docs.get("/docs/agents", (c) => { | |
| 1364 | const user = c.get("user"); | |
| 1365 | const idx = NAV_SECTIONS.findIndex((s) => s.href === "/docs/agents"); | |
| 1366 | const prev = NAV_SECTIONS[idx - 1]; | |
| 1367 | const next = NAV_SECTIONS[idx + 1]; | |
| 1368 | ||
| 1369 | return c.html( | |
| 1370 | <Layout title="Agent publishing — Docs — gluecron" user={user} description="Write, test, and publish an AI agent to the Gluecron marketplace. Earn 70% of every sale."> | |
| 1371 | <style dangerouslySetInnerHTML={{ __html: docsStyles }} /> | |
| 1372 | <div class="docs-wrap"> | |
| 1373 | <Sidebar current="/docs/agents" /> | |
| 1374 | <div class="docs-content"> | |
| 1375 | <h1>Agent publishing guide</h1> | |
| 1376 | <p class="docs-page-sub"> | |
| 1377 | Build an AI agent that drives Gluecron repos, publish it to the | |
| 1378 | marketplace, and earn <strong>70% revenue share</strong> on every | |
| 1379 | sale. Gluecron keeps 30% to cover infrastructure and fraud | |
| 1380 | prevention. | |
| 1381 | </p> | |
| 1382 | ||
| 1383 | <h2 id="overview">What is an agent?</h2> | |
| 1384 | <p> | |
| 1385 | A Gluecron agent is a JSON manifest that describes an AI tool-use | |
| 1386 | loop. It declares: | |
| 1387 | </p> | |
| 1388 | <ul> | |
| 1389 | <li>A name, description, and icon.</li> | |
| 1390 | <li> | |
| 1391 | Which MCP tools the agent is allowed to call (scoped to the | |
| 1392 | token provided by the buyer). | |
| 1393 | </li> | |
| 1394 | <li> | |
| 1395 | A system prompt that instructs Claude how to use those tools to | |
| 1396 | accomplish a goal. | |
| 1397 | </li> | |
| 1398 | <li> | |
| 1399 | Optional input fields the user fills in before the agent runs. | |
| 1400 | </li> | |
| 1401 | </ul> | |
| 1402 | <p> | |
| 1403 | Example agents: "Daily standup writer", "Auto-triage new issues", | |
| 1404 | "Dependency upgrade PR opener", "Release notes drafter". | |
| 1405 | </p> | |
| 1406 | ||
| 1407 | <h2 id="write">1. Write your agent</h2> | |
| 1408 | <p> | |
| 1409 | Create an <code>agent.json</code> in any repo: | |
| 1410 | </p> | |
| 1411 | <pre><code>{`{ | |
| 1412 | "name": "standup-writer", | |
| 1413 | "displayName": "Daily Standup Writer", | |
| 1414 | "description": "Reads yesterday's pushes and open PRs, then writes a concise standup summary.", | |
| 1415 | "icon": "https://example.com/standup-icon.png", | |
| 1416 | "version": "1.0.0", | |
| 1417 | "tools": [ | |
| 1418 | "gluecron_repo_list_issues", | |
| 1419 | "gluecron_list_prs", | |
| 1420 | "gluecron_repo_read_file" | |
| 1421 | ], | |
| 1422 | "inputs": [ | |
| 1423 | { | |
| 1424 | "key": "owner", | |
| 1425 | "label": "GitHub/Gluecron owner", | |
| 1426 | "type": "string", | |
| 1427 | "required": true | |
| 1428 | }, | |
| 1429 | { | |
| 1430 | "key": "repo", | |
| 1431 | "label": "Repository", | |
| 1432 | "type": "string", | |
| 1433 | "required": true | |
| 1434 | } | |
| 1435 | ], | |
| 1436 | "systemPrompt": "You are a standup writer. Given the open PRs and recent issues on the target repo, write a concise standup update in bullet-point form: what was done yesterday, what is planned today, and any blockers.", | |
| 1437 | "pricing": { | |
| 1438 | "model": "per_run", | |
| 1439 | "price_usd": 0.10 | |
| 1440 | } | |
| 1441 | }`}</code></pre> | |
| 1442 | ||
| 1443 | <h2 id="test">2. Test locally</h2> | |
| 1444 | <p> | |
| 1445 | Use the Gluecron CLI to run your agent against a repo before | |
| 1446 | publishing: | |
| 1447 | </p> | |
| 1448 | <pre><code>{`# Install the CLI | |
| 1449 | bun add -g @gluecron/cli | |
| 1450 | ||
| 1451 | # Run the agent locally (no marketplace, no billing) | |
| 1452 | gluecron agent run ./agent.json \\ | |
| 1453 | --input owner=my-org \\ | |
| 1454 | --input repo=api \\ | |
| 1455 | --token glc_your_dev_token`}</code></pre> | |
| 1456 | <p> | |
| 1457 | The CLI streams each tool call and its result to stdout so you | |
| 1458 | can see exactly what Claude is doing. Errors in the system prompt | |
| 1459 | or missing tool permissions surface here before any buyer sees | |
| 1460 | them. | |
| 1461 | </p> | |
| 1462 | ||
| 1463 | <h3>Testing checklist</h3> | |
| 1464 | <ul> | |
| 1465 | <li> | |
| 1466 | Run against a repo with open PRs and issues to verify the | |
| 1467 | agent handles non-empty state. | |
| 1468 | </li> | |
| 1469 | <li>Run against a brand-new empty repo (edge case).</li> | |
| 1470 | <li> | |
| 1471 | Confirm the agent only calls the tools it declared in{" "} | |
| 1472 | <code>"tools"</code> — the runtime rejects undeclared tool | |
| 1473 | calls. | |
| 1474 | </li> | |
| 1475 | <li> | |
| 1476 | Test with a read-only token to verify the agent degrades | |
| 1477 | gracefully when write tools are unavailable. | |
| 1478 | </li> | |
| 1479 | </ul> | |
| 1480 | ||
| 1481 | <h2 id="publish">3. Publish to the marketplace</h2> | |
| 1482 | <p> | |
| 1483 | Once you are happy with your agent, push <code>agent.json</code>{" "} | |
| 1484 | to a public repo and submit via the marketplace form: | |
| 1485 | </p> | |
| 1486 | <ol> | |
| 1487 | <li> | |
| 1488 | Visit{" "} | |
| 1489 | <a href="/marketplace/agents/new">/marketplace/agents/new</a>. | |
| 1490 | </li> | |
| 1491 | <li> | |
| 1492 | Paste the URL of your public repo (e.g.{" "} | |
| 1493 | <code>https://gluecron.com/you/standup-writer</code>). | |
| 1494 | </li> | |
| 1495 | <li> | |
| 1496 | Set a price — per-run, monthly subscription, or free. | |
| 1497 | Minimum per-run price is <strong>$0.01</strong>. | |
| 1498 | </li> | |
| 1499 | <li> | |
| 1500 | Submit for review. The Gluecron team checks that the agent | |
| 1501 | manifest is valid, the system prompt doesn't exfiltrate data, | |
| 1502 | and the declared tool scope is appropriate. Reviews typically | |
| 1503 | complete within <strong>48 hours</strong>. | |
| 1504 | </li> | |
| 1505 | </ol> | |
| 1506 | ||
| 1507 | <h2 id="revenue">Revenue share</h2> | |
| 1508 | <table> | |
| 1509 | <thead> | |
| 1510 | <tr> | |
| 1511 | <th>Party</th> | |
| 1512 | <th>Share</th> | |
| 1513 | </tr> | |
| 1514 | </thead> | |
| 1515 | <tbody> | |
| 1516 | <tr> | |
| 1517 | <td>Agent author (you)</td> | |
| 1518 | <td><strong>70%</strong></td> | |
| 1519 | </tr> | |
| 1520 | <tr> | |
| 1521 | <td>Gluecron</td> | |
| 1522 | <td>30% (infrastructure + payment processing + fraud prevention)</td> | |
| 1523 | </tr> | |
| 1524 | </tbody> | |
| 1525 | </table> | |
| 1526 | <p> | |
| 1527 | Payouts are processed monthly via Stripe to the bank account or | |
| 1528 | PayPal address on file in your{" "} | |
| 1529 | <a href="/settings">account settings</a>. Minimum payout | |
| 1530 | threshold is <strong>$10</strong>. | |
| 1531 | </p> | |
| 1532 | ||
| 1533 | <h2 id="versioning">Versioning and updates</h2> | |
| 1534 | <p> | |
| 1535 | Bump <code>"version"</code> in <code>agent.json</code>, push, | |
| 1536 | and submit an update from the marketplace dashboard. Buyers on | |
| 1537 | a subscription get the new version automatically on their next | |
| 1538 | run. Per-run buyers always get the latest approved version. | |
| 1539 | </p> | |
| 1540 | <p> | |
| 1541 | Breaking changes (removing tools, changing input keys) require a | |
| 1542 | major version bump and a migration note in the changelog field. | |
| 1543 | </p> | |
| 1544 | ||
| 1545 | <h2 id="guidelines">Marketplace guidelines</h2> | |
| 1546 | <ul> | |
| 1547 | <li> | |
| 1548 | Agents must not exfiltrate repo content to external URLs not | |
| 1549 | disclosed in the description. | |
| 1550 | </li> | |
| 1551 | <li> | |
| 1552 | Agents must not call write tools without clear disclosure in | |
| 1553 | the description (e.g. "This agent opens PRs automatically"). | |
| 1554 | </li> | |
| 1555 | <li> | |
| 1556 | System prompts are public — do not embed secrets or proprietary | |
| 1557 | data in them. | |
| 1558 | </li> | |
| 1559 | <li> | |
| 1560 | Agents that repeatedly fail (error rate > 20% over 7 days) | |
| 1561 | are automatically delisted pending author review. | |
| 1562 | </li> | |
| 1563 | </ul> | |
| 1564 | ||
| 1565 | <h2 id="example-agent">Full example: dependency upgrade agent</h2> | |
| 1566 | <pre><code>{`{ | |
| 1567 | "name": "dep-upgrader", | |
| 1568 | "displayName": "Dependency Upgrade PR", | |
| 1569 | "description": "Checks for outdated npm/bun dependencies and opens a PR with the bumped package.json.", | |
| 1570 | "version": "1.0.0", | |
| 1571 | "tools": [ | |
| 1572 | "gluecron_repo_read_file", | |
| 1573 | "gluecron_create_pr", | |
| 1574 | "gluecron_comment_pr" | |
| 1575 | ], | |
| 1576 | "inputs": [ | |
| 1577 | { "key": "owner", "label": "Owner", "type": "string", "required": true }, | |
| 1578 | { "key": "repo", "label": "Repo", "type": "string", "required": true } | |
| 1579 | ], | |
| 1580 | "systemPrompt": "You are a dependency upgrade bot. 1) Read package.json from the repo. 2) Identify any dependencies pinned to a major version older than the current latest on npm. 3) Produce an updated package.json with the bumped versions. 4) Open a PR titled 'chore(deps): bump outdated dependencies' with the updated file as the diff. 5) Comment on the PR with a table of what changed.", | |
| 1581 | "pricing": { | |
| 1582 | "model": "per_run", | |
| 1583 | "price_usd": 0.25 | |
| 1584 | } | |
| 1585 | }`}</code></pre> | |
| 1586 | ||
| 1587 | <PageNav prev={prev} next={next} /> | |
| 1588 | <EditLink path="src/routes/docs.tsx" /> | |
| 1589 | </div> | |
| 1590 | </div> | |
| 1591 | </Layout> | |
| 1592 | ); | |
| 1593 | }); | |
| 1594 | ||
| 1595 | export default docs; |