diff --git a/packages/cli/src/capture.ts b/packages/cli/src/capture.ts index c9af7e1..ffc1897 100644 --- a/packages/cli/src/capture.ts +++ b/packages/cli/src/capture.ts @@ -51,6 +51,7 @@ import { getLastDeltaForFile, } from "./lib/delta"; import type { AiAgent } from "./lib/types"; +import { writeHookTrace } from "./lib/hooks"; // ============================================================================= // Types @@ -232,6 +233,8 @@ async function setupCaptureContext( conversationId: string, model: string | null ): Promise { + // this will override the config used before we had a payload from the agent. Once we have a payload, + // prefer the paths indicated in the payload. const repoRoot = await findRepoRoot(filePath); if (!repoRoot) { if (process.env.AGENTBLAME_DEBUG) { @@ -819,9 +822,25 @@ export async function runCapture(): Promise { } if (!input.trim()) { + console.warn(`[agentblame] Capture: input empty, returning 0`) process.exit(0); } + // make best-effort to load an existing config using cwd- payload may override this config dir later in the process + const repoRoot = await findRepoRoot(process.cwd()); + if (!repoRoot) { + console.warn(`[agentblame] Capture: could not detect repo root via cwd, configs may not work until we get a payload`) + } + + const traceHooks = repoRoot ? await getConfig(repoRoot, 'traceHooks') : false; + + if (traceHooks && repoRoot) { + const traceFile = writeHookTrace(repoRoot, input); + if (traceFile && process.env.AGENTBLAME_DEBUG) { + console.error(`[agentblame] Trace written: ${traceFile}`); + } + } + const data = JSON.parse(input); const payload = data.payload || data; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 3349cc6..3b86c82 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -252,7 +252,10 @@ async function cleanupGlobalInstall(): Promise<{ } async function runInit(initArgs: string[] = []): Promise { + // TODO: map these CL switches to config value enum keys const forceCleanup = initArgs.includes("--force") || initArgs.includes("-f"); + const traceHooks = initArgs.includes("--trace-hooks"); + const storePromptContent = initArgs.includes("--store-prompt-content"); // Check if Bun is installed (required for hooks) if (!isBunInstalled()) { @@ -318,6 +321,11 @@ async function runInit(initArgs: string[] = []): Promise { setDatabasePath(dbPath); initDatabase(); results.push({ name: "Database (.git/agentblame/)", success: true }); + if (traceHooks || storePromptContent) { + await setConfig(repoRoot, 'traceHooks', traceHooks); + await setConfig(repoRoot, 'storePromptContent', storePromptContent); + results.push({ name: `Config values`, success: true }); + } } catch (err) { results.push({ name: "Database", success: false }); } diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index d2a8070..ef42b9b 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -13,10 +13,12 @@ import { runGit } from "./git/gitCli"; export interface AgentBlameConfig { /** Whether to store actual prompt content (default: true) */ storePromptContent: boolean; + traceHooks: boolean; } const CONFIG_DEFAULTS: AgentBlameConfig = { storePromptContent: false, // Default: don't store prompt text (privacy-first) + traceHooks: false, // don't leave debug trace files around unless user opts in }; const CONFIG_PREFIX = "agentblame"; diff --git a/packages/cli/src/lib/hooks.ts b/packages/cli/src/lib/hooks.ts index f699a3c..e98eac0 100644 --- a/packages/cli/src/lib/hooks.ts +++ b/packages/cli/src/lib/hooks.ts @@ -786,3 +786,35 @@ export async function uninstallGitHubAction(repoRoot: string): Promise return false; } } + + +/** + * Write a trace file and log entry + * @param agentblameDir - The .agentblame directory + * @param input - The raw input string to trace + * @returns The trace filename if written, null if tracing disabled + */ +export function writeHookTrace(repoRoot: string, input: string): string | null { + + // Create traces directory if needed + const agentblameDir = path.join(repoRoot, ".agentblame"); + const tracesDir = path.join(agentblameDir, "traces"); + if (!fs.existsSync(tracesDir)) { + fs.mkdirSync(tracesDir, { recursive: true }); + } + + // Generate filename with timestamp + const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); + const filename = `${timestamp}.json`; + const tracePath = path.join(tracesDir, filename); + + // Write the trace file + fs.writeFileSync(tracePath, input, "utf8"); + + // Append to debug.log + const logPath = path.join(tracesDir, "traces.log"); + const logLine = `${new Date().toISOString()} ${filename}\n`; + fs.appendFileSync(logPath, logLine, "utf8"); + + return filename; +}