import type { ExtensionAPI } from '@earendil-works/pi-coding-agent'; import * as fs from 'node:fs'; import * as path from 'node:path'; import { execSync } from 'node:child_process'; function readJsonIfExists(filePath: string): T | undefined { try { const raw = fs.readFileSync(filePath, 'utf8'); return JSON.parse(raw) as T; } catch { return undefined; } } function isAutoUpdateEnabled(projectDir: string): boolean { for (const dirName of ['.ua', '.understand-anything']) { const configPath = path.join(projectDir, dirName, 'config.json'); const config = readJsonIfExists<{ autoUpdate?: boolean }>(configPath); if (config?.autoUpdate === true) return true; } return false; } function hasKnowledgeGraph(projectDir: string): boolean { for (const dirName of ['.ua', '.understand-anything']) { if (fs.existsSync(path.join(projectDir, dirName, 'knowledge-graph.json'))) { return true; } } return false; } function getCurrentCommit(cwd: string): string | undefined { try { return execSync('git rev-parse HEAD', { cwd, encoding: 'utf8' }).trim(); } catch { return undefined; } } function getGraphCommit(projectDir: string): string | undefined { for (const dirName of ['.ua', '.understand-anything']) { const metaPath = path.join(projectDir, dirName, 'meta.json'); const meta = readJsonIfExists<{ gitCommitHash?: string }>(metaPath); if (meta?.gitCommitHash) return meta.gitCommitHash; } return undefined; } function isGitMutation(cmd: string): boolean { return /git\s+(commit|merge|cherry-pick|rebase)/.test(cmd); } export default function autoUpdateHook(pi: ExtensionAPI): void { // On bash tool result, detect git mutations and prompt for an update. pi.on('tool_result', async (event, ctx) => { if (event.toolName !== 'bash') return; const cmd = String(event.input?.command ?? ''); if (!isGitMutation(cmd)) return; const projectDir = ctx.cwd; if (!isAutoUpdateEnabled(projectDir)) return; if (!hasKnowledgeGraph(projectDir)) return; const message = '[understand-anything] Git mutation detected with auto-update enabled. Run /understand to update the knowledge graph.'; if (ctx.ui) { ctx.ui.setStatus('understand-anything', message); } }); // On user bash (!/!!), detect git mutations and prompt for an update. pi.on('user_bash', async (event, ctx) => { if (!isGitMutation(event.command)) return; const projectDir = ctx.cwd; if (!isAutoUpdateEnabled(projectDir)) return; if (!hasKnowledgeGraph(projectDir)) return; const message = '[understand-anything] Git mutation detected with auto-update enabled. Run /understand to update the knowledge graph.'; if (ctx.ui) { ctx.ui.setStatus('understand-anything', message); } }); // On session start, check whether the graph is stale and notify the user. pi.on('session_start', async (event, ctx) => { const projectDir = ctx.cwd; if (!isAutoUpdateEnabled(projectDir)) return; if (!hasKnowledgeGraph(projectDir)) return; const graphCommit = getGraphCommit(projectDir); const currentCommit = getCurrentCommit(projectDir); if (!graphCommit || !currentCommit || graphCommit === currentCommit) return; const message = '[understand-anything] Knowledge graph is stale. Run /understand to update it.'; if (ctx.ui) { ctx.ui.setStatus('understand-anything', message); } }); }