Files
gstack/browse/src/project-slug.ts
Garry Tan b0d1a9b2e9 feat(browse): telemetry signals + project-slug helper
Lightweight telemetry per DX D9: piggybacks on ~/.gstack/analytics/ pattern.
Hostname + aggregate counters only, no body content. GSTACK_TELEMETRY_OFF=1
silences. Fire-and-forget — never blocks calling path.

Signals fired so far:
- domain_skill_saved {host, scope, state, bytes}
- domain_skill_save_blocked {host, reason}

(domain_skill_fired and cdp_method_* fired in subsequent commits.)

Also extracts project-slug resolution into project-slug.ts so server.ts
and domain-skill-commands.ts share one cached lookup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 13:12:16 -07:00

37 lines
1.0 KiB
TypeScript

/**
* Project slug resolution for the browse daemon.
*
* Used by domain-skills (per-project storage) and sidebar prompt-context
* injection. Cached after first call — slug is derived from the daemon's
* git remote (or env override) and doesn't change between commands.
*/
import * as path from 'path';
import * as os from 'os';
import { execSync } from 'child_process';
let cachedSlug: string | null = null;
export function getCurrentProjectSlug(): string {
if (cachedSlug) return cachedSlug;
const explicit = process.env.GSTACK_PROJECT_SLUG;
if (explicit) {
cachedSlug = explicit;
return explicit;
}
try {
const slugBin = path.join(os.homedir(), '.claude/skills/gstack/bin/gstack-slug');
const out = execSync(slugBin, { encoding: 'utf8', timeout: 2000 }).trim();
const m = out.match(/SLUG="?([^"\n]+)"?/);
cachedSlug = m ? m[1]! : (out || 'unknown');
} catch {
cachedSlug = 'unknown';
}
return cachedSlug;
}
/** Reset cache; for tests only. */
export function _resetProjectSlugCache(): void {
cachedSlug = null;
}