aboutsummaryrefslogtreecommitdiffstats
path: root/examples/pi/atuin.ts
blob: 55c17cb8753e981c5b4416a521777c11470e6ca4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
 * Atuin extension for pi.
 *
 * Tracks bash commands executed by pi in Atuin history with author `pi`.
 *
 * Install with:
 *   atuin hook install pi
 *
 * Then restart pi or run /reload.
 */

import type { BashOperations, ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { createBashTool, createLocalBashOperations } from "@mariozechner/pi-coding-agent";

const ATUIN_AUTHOR = "pi";
const ATUIN_TIMEOUT_MS = 10_000;

async function startHistory(
	pi: ExtensionAPI,
	cwd: string,
	command: string,
): Promise<string | undefined> {
	try {
		const result = await pi.exec(
			"atuin",
			["history", "start", "--author", ATUIN_AUTHOR, "--", command],
			{ cwd, timeout: ATUIN_TIMEOUT_MS },
		);

		if (result.code !== 0) return undefined;

		const id = result.stdout.trim();
		return id.length > 0 ? id : undefined;
	} catch {
		return undefined;
	}
}

async function endHistory(
	pi: ExtensionAPI,
	cwd: string,
	historyId: string,
	exitCode: number,
): Promise<void> {
	try {
		await pi.exec(
			"atuin",
			["history", "end", historyId, "--exit", String(exitCode)],
			{ cwd, timeout: ATUIN_TIMEOUT_MS },
		);
	} catch {
		// Ignore Atuin failures so command execution is never blocked.
	}
}

export default function atuinPiExtension(pi: ExtensionAPI) {
	const cwd = process.cwd();
	const local = createLocalBashOperations();

	const trackedOperations: BashOperations = {
		async exec(command, commandCwd, options) {
			const historyId = await startHistory(pi, commandCwd, command);
			let exitCode: number | null = null;

			try {
				const result = await local.exec(command, commandCwd, options);
				exitCode = result.exitCode;
				return result;
			} finally {
				if (historyId) {
					await endHistory(
						pi,
						commandCwd,
						historyId,
						exitCode ?? (options.signal?.aborted ? 130 : 1),
					);
				}
			}
		},
	};

	pi.registerTool(
		createBashTool(cwd, {
			operations: trackedOperations,
		}),
	);
}