Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit19cbf20

feat(onboarding): the Connect Claude page now points at a first real task

feat(onboarding): the Connect Claude page now points at a first real task

Connecting used to be the end of this page. A user finished setup with a
working MCP connection, a grid of 15+ tools, and no indication of what to do
with any of it — agent-first onboarding that stopped at "the agent can
technically reach your account".

Adds a fourth step listing the user's three most recently updated
repositories, each with a direct link to /:owner/:repo/spec — the one action
that turns a plain-English description into a reviewed, gated pull request.
Each row also reports how many of its files are semantically indexed, because
that number decides whether the agent can search the codebase or is working
blind; it links the fix shipped in 5d13cf5 to something the user can see.

Nothing new was built: the token mint, the tool surface and spec-to-PR all
existed. As with import-bulk and POST /new earlier in this session, the
capability was present and nothing connected it to the path a user actually
walks.

Best-effort — the repo query is wrapped, because this page's job is getting
Claude connected and it must still render if that query fails. The section is
omitted entirely when the user has no repositories rather than showing an
empty shell.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ccantynz-alt committed on July 28, 2026Parent: 5d13cf5
1 file changed+111019cbf2003ff308dd0bddf1dab236bb62192c8fea
1 changed file+111−0
Modifiedsrc/routes/connect-claude.tsx+111−0View fileUnifiedSplit
565565 border-color: var(--border-strong);
566566 }
567567
568 /* ─── First-task repo list (Step 4) ─── */
569 .connect-claude-repos {
570 list-style: none;
571 margin: var(--space-4) 0 0;
572 padding: 0;
573 display: flex;
574 flex-direction: column;
575 gap: 8px;
576 }
577 .connect-claude-repo {
578 display: flex;
579 align-items: center;
580 justify-content: space-between;
581 gap: var(--space-3);
582 padding: 14px 16px;
583 border: 1px solid var(--border);
584 border-radius: 12px;
585 background: var(--bg-elevated);
586 flex-wrap: wrap;
587 }
588 .connect-claude-repo-main { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
589 .connect-claude-repo-name {
590 font-family: var(--font-display);
591 font-size: 15px;
592 font-weight: 650;
593 color: var(--text-strong);
594 text-decoration: none;
595 }
596 .connect-claude-repo-name:hover { color: var(--accent); }
597 .connect-claude-repo-meta {
598 font-size: 12.5px;
599 color: var(--text-muted);
600 font-variant-numeric: tabular-nums;
601 }
602
568603 /* ─── Tools grid (Step 3) ─── */
569604 .connect-claude-tools-group {
570605 margin-top: var(--space-3);
770805connectClaude.get("/connect/claude", async (c) => {
771806 const user = c.get("user")!;
772807 const tools = buildToolsView();
808
809 // The user's repositories, with how much of each is semantically indexed —
810 // that number is what decides whether the agent can actually search the
811 // codebase or is working blind. Best-effort: this page's job is to get
812 // Claude connected, and it must still render if the query fails.
813 let firstTaskRepos: Array<{ name: string; indexedFiles: number }> = [];
814 try {
815 // code_embeddings is queried as a correlated subquery rather than a join
816 // so a repo with no embeddings still returns a row (with 0).
817 const { repositories } = await import("../db/schema");
818 const { eq: eqOp, sql: sqlOp, desc: descOp } = await import("drizzle-orm");
819 const rows = await db
820 .select({
821 name: repositories.name,
822 indexedFiles: sqlOp<number>`(
823 SELECT count(*)::int FROM code_embeddings
824 WHERE code_embeddings.repository_id = ${repositories.id}
825 )`,
826 })
827 .from(repositories)
828 .where(eqOp(repositories.ownerId, user.id))
829 .orderBy(descOp(repositories.updatedAt))
830 .limit(3);
831 firstTaskRepos = rows.map((r) => ({
832 name: r.name,
833 indexedFiles: Number(r.indexedFiles ?? 0),
834 }));
835 } catch (err) {
836 console.error("[connect-claude] first-task repos query failed:", err);
837 }
773838 const host = config.appBaseUrl || "https://gluecron.com";
774839
775840 // Best-effort: look up the most recent MCP audit row for this user.
10481113 </div>
10491114 </div>
10501115 </section>
1116
1117 {/* ─── Step 4: the first real task ───────────────────────────────
1118 Connecting used to be the end of this page: a working MCP
1119 connection, a tools grid, and no indication of what to do with
1120 any of it. Agent-first onboarding means the agent does something
1121 real, so point at the user's own repositories and the one action
1122 that produces a pull request. */}
1123 {firstTaskRepos.length > 0 && (
1124 <section class="connect-claude-section">
1125 <div class="connect-claude-step-head">
1126 <span class="connect-claude-step-num">4</span>
1127 <div>
1128 <h2 class="connect-claude-step-title">Give it something to do</h2>
1129 <p class="connect-claude-step-sub">
1130 Describe a change in plain English and the agent opens the
1131 pull request — reviewed and gated like any other.
1132 </p>
1133 </div>
1134 </div>
1135 <ul class="connect-claude-repos">
1136 {firstTaskRepos.map((r) => (
1137 <li class="connect-claude-repo">
1138 <div class="connect-claude-repo-main">
1139 <a
1140 class="connect-claude-repo-name"
1141 href={`/${user.username}/${r.name}`}
1142 >
1143 {r.name}
1144 </a>
1145 <span class="connect-claude-repo-meta">
1146 {r.indexedFiles > 0
1147 ? `${r.indexedFiles} files indexed — the agent can search this repo`
1148 : "Not indexed yet — push once and search comes online"}
1149 </span>
1150 </div>
1151 <a
1152 class="btn btn-primary"
1153 href={`/${user.username}/${r.name}/spec`}
1154 >
1155 Build something
1156 </a>
1157 </li>
1158 ))}
1159 </ul>
1160 </section>
1161 )}
10511162 </div>
10521163 <script dangerouslySetInnerHTML={{ __html: clientScript() }} />
10531164 </Layout>
10541165