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

extension.ts

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

extension.tsBlame183 lines · 1 contributor
63fe719Claude1/**
2 * Gluecron VS Code extension entry point.
3 *
4 * Activation registers:
5 * - sign in / sign out commands (PAT stored in vscode.SecretStorage)
6 * - sidebar chat + pulls + issues + standups (iframe-embedded webviews)
7 * - "open current file in Gluecron" + "open PRs" deep-links
8 * - "ship spec" + "voice to PR" host shortcuts
9 * - "generate AI commit message" — wires up the SCM input box
10 */
11
12import * as vscode from "vscode";
13import { relative } from "node:path";
14import {
15 clearToken,
16 restoreSignedInContext,
17 signInFlow,
18} from "./auth";
19import {
20 getDefaultBranch,
21 getGluecronRepoInfo,
22 getHost,
23 hostUrl,
24 pickWorkspaceFolder,
25} from "./repo";
26import { buildBlobUrl } from "./git";
27import {
28 chatViewProvider,
29 issuesViewProvider,
30 pullsViewProvider,
31 standupsViewProvider,
32} from "./sidebar/iframe-view";
33import { runAiCommitMessage } from "./commands/ai-commit";
34
35export async function activate(context: vscode.ExtensionContext): Promise<void> {
36 const output = vscode.window.createOutputChannel("Gluecron");
37 context.subscriptions.push(output);
38 output.appendLine("Gluecron activated.");
39
40 await restoreSignedInContext(context);
41
42 // ── Commands ──────────────────────────────────────────────────────────
43
44 context.subscriptions.push(
45 vscode.commands.registerCommand("gluecron.signIn", async () => {
46 await signInFlow(context, getHost());
47 })
48 );
49
50 context.subscriptions.push(
51 vscode.commands.registerCommand("gluecron.signOut", async () => {
52 await clearToken(context);
53 vscode.window.showInformationMessage("Gluecron: signed out.");
54 })
55 );
56
57 context.subscriptions.push(
58 vscode.commands.registerCommand("gluecron.chatWithRepo", async () => {
59 // Reveal the sidebar then focus the chat view.
60 await vscode.commands.executeCommand("workbench.view.extension.gluecron-sidebar");
61 await vscode.commands.executeCommand("gluecron.chat.focus");
62 })
63 );
64
65 context.subscriptions.push(
66 vscode.commands.registerCommand("gluecron.openInGluecron", async () => {
67 const editor = vscode.window.activeTextEditor;
68 const folder = pickWorkspaceFolder();
69 const info = await getGluecronRepoInfo();
70 if (!folder || !info) {
71 vscode.window.showWarningMessage(
72 "Gluecron: no Gluecron remote detected for this workspace."
73 );
74 return;
75 }
76 const filePath = editor
77 ? relative(folder.uri.fsPath, editor.document.uri.fsPath)
78 : "";
79 const branch = getDefaultBranch();
80 const url = filePath
81 ? buildBlobUrl(
82 getHost(),
83 info.owner,
84 info.repo,
85 branch,
86 filePath,
87 editor?.selection.active.line
88 )
89 : hostUrl(`/${info.owner}/${info.repo}`);
90 await vscode.env.openExternal(vscode.Uri.parse(url));
91 })
92 );
93
94 context.subscriptions.push(
95 vscode.commands.registerCommand("gluecron.openPRs", async () => {
96 const info = await getGluecronRepoInfo();
97 const url = info
98 ? hostUrl(`/${info.owner}/${info.repo}/pulls`)
99 : hostUrl("/pulls");
100 await vscode.env.openExternal(vscode.Uri.parse(url));
101 })
102 );
103
104 context.subscriptions.push(
105 vscode.commands.registerCommand("gluecron.openIssues", async () => {
106 const info = await getGluecronRepoInfo();
107 const url = info
108 ? hostUrl(`/${info.owner}/${info.repo}/issues`)
109 : hostUrl("/issues");
110 await vscode.env.openExternal(vscode.Uri.parse(url));
111 })
112 );
113
114 context.subscriptions.push(
115 vscode.commands.registerCommand("gluecron.openStandups", async () => {
116 const info = await getGluecronRepoInfo();
117 const url = info
118 ? hostUrl(`/${info.owner}/${info.repo}/standups`)
119 : hostUrl("/standups");
120 await vscode.env.openExternal(vscode.Uri.parse(url));
121 })
122 );
123
124 context.subscriptions.push(
125 vscode.commands.registerCommand("gluecron.shipSpec", async () => {
126 const editor = vscode.window.activeTextEditor;
127 const folder = pickWorkspaceFolder();
128 const info = await getGluecronRepoInfo();
129 if (!editor || !folder || !info) {
130 vscode.window.showWarningMessage(
131 "Gluecron: open a file inside a Gluecron-hosted repo first."
132 );
133 return;
134 }
135 const rel = relative(folder.uri.fsPath, editor.document.uri.fsPath);
136 const url = hostUrl(
137 `/${info.owner}/${info.repo}/specs/new?path=${encodeURIComponent(rel)}`
138 );
139 await vscode.env.openExternal(vscode.Uri.parse(url));
140 })
141 );
142
143 context.subscriptions.push(
144 vscode.commands.registerCommand("gluecron.voiceToPR", async () => {
145 await vscode.env.openExternal(vscode.Uri.parse(hostUrl("/voice")));
146 })
147 );
148
149 context.subscriptions.push(
150 vscode.commands.registerCommand("gluecron.aiCommitMessage", async () => {
151 await runAiCommitMessage(context);
152 })
153 );
154
155 // ── Sidebar views ─────────────────────────────────────────────────────
156
157 context.subscriptions.push(
158 vscode.window.registerWebviewViewProvider(
159 "gluecron.chat",
160 chatViewProvider(),
161 { webviewOptions: { retainContextWhenHidden: true } }
162 ),
163 vscode.window.registerWebviewViewProvider(
164 "gluecron.pulls",
165 pullsViewProvider(),
166 { webviewOptions: { retainContextWhenHidden: true } }
167 ),
168 vscode.window.registerWebviewViewProvider(
169 "gluecron.issues",
170 issuesViewProvider(),
171 { webviewOptions: { retainContextWhenHidden: true } }
172 ),
173 vscode.window.registerWebviewViewProvider(
174 "gluecron.standups",
175 standupsViewProvider(),
176 { webviewOptions: { retainContextWhenHidden: true } }
177 )
178 );
179}
180
181export function deactivate(): void {
182 // nothing — VS Code cleans up registered subscriptions automatically.
183}