Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ async function main() {
await installGit();

// Initialize error tracking and telemetry (no-ops if opted out).
// Telemetry resolves repo context asynchronously — we don't block on it;
// any events emitted before it finishes simply won't carry repo_id.
// Await telemetry so the repo context is resolved before the preAction
// hook fires `command_executed` — otherwise that event always lands
// without `repo_id`. The repo lookup is a few cached git subprocesses
// (~5–20ms on a cold run) and runs exactly once per invocation.
initSentry();
void initTelemetry();
await initTelemetry();

const logLevelOption = new Option("--log-level <level>", "Set log verbosity")
.choices(["error", "warn", "info", "debug"] as const)
Expand Down
31 changes: 18 additions & 13 deletions src/helpers/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,13 @@ export async function getRepoContext(): Promise<RepoContext> {
return cached;
}

const remoteUrl = await runGitCapture(
["config", "--get", "remote.origin.url"],
cwd
);
const defaultBranch = await resolveDefaultBranch(cwd);
// Run the remaining probes concurrently — they're independent and the
// dominant cost on Windows is serial subprocess spawn (~25ms each), not
// the git work itself.
const [remoteUrl, defaultBranch] = await Promise.all([
runGitCapture(["config", "--get", "remote.origin.url"], cwd),
resolveDefaultBranch(cwd),
]);

if (!remoteUrl) {
cached = {
Expand Down Expand Up @@ -301,18 +303,21 @@ async function runGitCheck(args: string[], cwd: string): Promise<boolean> {
}

async function resolveDefaultBranch(cwd: string): Promise<string | null> {
// Prefer the remote HEAD symbolic ref (e.g., `origin/main`)
const symRef = await runGitCapture(
["symbolic-ref", "--short", "refs/remotes/origin/HEAD"],
cwd
);
// Fire the preferred lookup (remote HEAD symbolic ref) and the fallback
// (currently-checked-out branch) in parallel. The fallback subprocess is
// cheap and lets us hide its spawn cost behind the other concurrent git
// calls — serial, it was the tail on the "no remote HEAD" path.
const [symRef, currentBranch] = await Promise.all([
runGitCapture(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], cwd),
runGitCapture(["rev-parse", "--abbrev-ref", "HEAD"], cwd),
]);
if (symRef) {
const slash = symRef.indexOf("/");
return slash >= 0 ? symRef.slice(slash + 1) : symRef;
}
// Fallback: whatever branch is currently checked out. Not strictly the
// "default" branch, but better than null for a single-user local repo.
return runGitCapture(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
// Fallback: not strictly the "default" branch, but better than null for
// a single-user local repo.
return currentBranch;
}

// ---------------------------------------------------------------------------
Expand Down
6 changes: 4 additions & 2 deletions src/helpers/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ function getCommonProperties(): Record<string, unknown> {
* If telemetry is disabled, this is a no-op and all subsequent calls are too.
*
* Returns a promise that resolves once the async repo-context lookup is done.
* Callers can safely `trackEvent()` before awaiting — events emitted during
* the window before repo resolution just won't include `repo_id` / `repo_host`.
* Callers should `await` before emitting events so every event carries
* `repo_id` / `repo_host` — emitting before the await resolves means the
* event ships without repo identity. The repo lookup runs a handful of git
* subprocesses (cached per-process), so the added startup latency is small.
*/
export function initTelemetry(): Promise<void> {
if (!isTelemetryEnabled()) {
Expand Down
Loading