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
5 changes: 5 additions & 0 deletions .github/workflows/agentblame.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v1

- name: Configure git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Install dependencies
run: bun install

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "agentblame",
"displayName": "Agent Blame",
"description": "Track AI-generated vs human-written code. Provides git notes storage, CLI, and GitHub PR attribution.",
"version": "0.2.3",
"version": "0.2.5",
"private": true,
"license": "Apache-2.0",
"repository": {
Expand Down
20 changes: 4 additions & 16 deletions packages/chrome/build-chrome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,29 +71,17 @@ async function bundle(): Promise<void> {
});
console.log("✓ Bundled popup.js");

// Bundle content script
// Bundle unified content script router
await build({
entryPoints: [join(SRC_DIR, "content", "content.ts")],
entryPoints: [join(SRC_DIR, "content", "router.ts")],
bundle: true,
outfile: join(DIST_DIR, "content", "content.js"),
outfile: join(DIST_DIR, "content", "router.js"),
format: "iife",
target: "chrome100",
minify: false,
sourcemap: true,
});
console.log("✓ Bundled content.js");

// Bundle analytics entry script (for repo pages)
await build({
entryPoints: [join(SRC_DIR, "content", "analytics-entry.ts")],
bundle: true,
outfile: join(DIST_DIR, "content", "analytics-entry.js"),
format: "iife",
target: "chrome100",
minify: false,
sourcemap: true,
});
console.log("✓ Bundled analytics-entry.js");
console.log("✓ Bundled router.js");

// Bundle background service worker
await build({
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agentblame/chrome",
"version": "0.2.3",
"version": "0.2.5",
"description": "Agent Blame Chrome Extension - See AI attribution on GitHub PRs",
"private": true,
"scripts": {
Expand Down
116 changes: 61 additions & 55 deletions packages/chrome/src/content/analytics-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,75 +12,55 @@ import {
handleHashChange,
} from "./analytics-tab";

let observer: MutationObserver | null = null;
// Track if we've already set up listeners (avoid duplicates)
let listenersInitialized = false;

/**
* Initialize analytics sidebar injection
* Check if current URL could navigate to insights pages
* (i.e., we're on a repo page)
*/
function init(): void {
console.log("[Agent Blame] Analytics entry loaded on:", window.location.href);
console.log("[Agent Blame] isInsightsPage:", isInsightsPage());

if (isInsightsPage()) {
// Wait a bit for GitHub to fully render the page
setTimeout(() => {
console.log("[Agent Blame] Injecting sidebar item...");
injectSidebarItem();
}, 500);
}

// Watch for DOM changes (GitHub uses dynamic rendering)
setupObserver();

// Handle hash changes for navigation
setupHashListener();

// Handle GitHub's Turbo navigation
setupTurboListener();
function isRepoPage(): boolean {
// Match: github.com/owner/repo or github.com/owner/repo/*
return /^https:\/\/github\.com\/[^/]+\/[^/]+/.test(window.location.href);
}

/**
* Setup MutationObserver for dynamic content
* Initialize analytics sidebar injection
*/
function setupObserver(): void {
if (observer) {
observer.disconnect();
function init(): void {
// Quick exit if not on a repo page - no setup needed
if (!isRepoPage()) {
return;
}

observer = new MutationObserver(() => {
if (isInsightsPage()) {
injectSidebarItem();
} else {
removeSidebarItem();
}
});
// Only set up listeners once
if (listenersInitialized) {
return;
}
listenersInitialized = true;

// Observe the sidebar area for changes
const sidebar = document.querySelector(".Layout-sidebar");
if (sidebar) {
observer.observe(sidebar, { childList: true, subtree: true });
// On insights page - inject immediately
if (isInsightsPage()) {
setTimeout(() => injectSidebarItem(), 500);
}

// Also observe body for major page changes
observer.observe(document.body, {
childList: true,
subtree: false,
});
// Lightweight setup - only what's needed for navigation detection
setupHistoryListener();
setupHashListener();
setupTurboListener();
}

/**
* Handle hash changes (for #agent-blame navigation)
*/
function setupHashListener(): void {
window.addEventListener("hashchange", () => {
console.log("[Agent Blame] Hash changed to:", window.location.hash);
handleHashChange();
});

// Also handle popstate for back/forward
// Handle popstate for back/forward
window.addEventListener("popstate", () => {
console.log("[Agent Blame] Popstate - hash:", window.location.hash);
handleHashChange();
handleNavigation();
});
}

Expand All @@ -91,27 +71,53 @@ function setupTurboListener(): void {
// Turbo Drive fires these events on navigation
document.addEventListener("turbo:load", () => {
console.log("[Agent Blame] Turbo load");
if (isInsightsPage()) {
setTimeout(() => injectSidebarItem(), 100);
}
handleNavigation();
});

document.addEventListener("turbo:render", () => {
console.log("[Agent Blame] Turbo render");
if (isInsightsPage()) {
setTimeout(() => injectSidebarItem(), 100);
}
handleNavigation();
});

// Also handle the older pjax events (some GitHub pages still use these)
document.addEventListener("pjax:end", () => {
console.log("[Agent Blame] PJAX end");
if (isInsightsPage()) {
setTimeout(() => injectSidebarItem(), 100);
}
handleNavigation();
});
}

/**
* Handle navigation by intercepting History API
*/
function setupHistoryListener(): void {
// Intercept pushState
const originalPushState = history.pushState.bind(history);
history.pushState = (...args) => {
originalPushState(...args);
console.log("[Agent Blame] pushState:", window.location.href);
setTimeout(handleNavigation, 100);
};

// Intercept replaceState
const originalReplaceState = history.replaceState.bind(history);
history.replaceState = (...args) => {
originalReplaceState(...args);
console.log("[Agent Blame] replaceState:", window.location.href);
setTimeout(handleNavigation, 100);
};
}

/**
* Handle navigation - inject or remove sidebar item based on current page
*/
function handleNavigation(): void {
if (isInsightsPage()) {
setTimeout(() => injectSidebarItem(), 200);
} else {
removeSidebarItem();
}
}

// Initialize when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
Expand Down
Loading