Compare commits
3
Commits
75dcd21776
...
fccb75298e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fccb75298e | ||
|
|
3208abe464 | ||
|
|
08c7ca752a |
+50
-13
@@ -33,7 +33,32 @@
|
||||
],
|
||||
"defaultMode": "auto"
|
||||
},
|
||||
"model": "opus[1m]",
|
||||
"disableClaudeAiConnectors": true,
|
||||
"remoteControlAtStartup": false,
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "atuin hook claude-code"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUseFailure": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "atuin hook claude-code"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
@@ -47,22 +72,33 @@
|
||||
"command": "~/.claude/hooks/block-file-deletion.sh"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "atuin hook claude-code"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"disableWorkflows": true,
|
||||
"disableArtifact": true,
|
||||
"statusLine": {
|
||||
"type": "command",
|
||||
"command": "bash /home/tgrosinger/.claude/statusline-command.sh"
|
||||
},
|
||||
"enabledPlugins": {
|
||||
"pr-review-toolkit@claude-plugins-official": true,
|
||||
"gopls-lsp@claude-plugins-official": true,
|
||||
"frontend-design@claude-plugins-official": true,
|
||||
"code-simplifier@claude-plugins-official": true,
|
||||
"skill-creator@claude-plugins-official": true,
|
||||
"typescript-lsp@claude-plugins-official": true,
|
||||
"claude-md-management@claude-plugins-official": true,
|
||||
"security-guidance@claude-plugins-official": true
|
||||
"code-simplifier@claude-plugins-official": true,
|
||||
"frontend-design@claude-plugins-official": true,
|
||||
"gopls-lsp@claude-plugins-official": true,
|
||||
"pr-review-toolkit@claude-plugins-official": true,
|
||||
"security-guidance@claude-plugins-official": true,
|
||||
"skill-creator@claude-plugins-official": true,
|
||||
"typescript-lsp@claude-plugins-official": true
|
||||
},
|
||||
"sandbox": {
|
||||
"enabled": true,
|
||||
@@ -86,19 +122,23 @@
|
||||
"filesystem": {
|
||||
"allowWrite": [
|
||||
"~/.local/share/pnpm",
|
||||
"~/.cache/pnpm"
|
||||
"~/.cache/pnpm",
|
||||
"~/Documents/Atrium"
|
||||
],
|
||||
"denyRead": [
|
||||
"~/.ssh",
|
||||
"~/.config/Signal",
|
||||
"~/Documents"
|
||||
],
|
||||
"allowRead": [
|
||||
"~/Documents/Atrium"
|
||||
]
|
||||
},
|
||||
"excludedCommands": [
|
||||
"git push *",
|
||||
"brew *",
|
||||
"devbox add *",
|
||||
"nix *"
|
||||
"tn *",
|
||||
"obsidian *"
|
||||
]
|
||||
},
|
||||
"spinnerVerbs": {
|
||||
@@ -119,8 +159,5 @@
|
||||
"theme": "light",
|
||||
"editorMode": "vim",
|
||||
"agentPushNotifEnabled": true,
|
||||
"disableClaudeAiConnectors": true,
|
||||
"disableWorkflows": true,
|
||||
"disableArtifact": true,
|
||||
"skipAutoPermissionPrompt": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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 { ExtensionAPI, ExtensionContext } 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.
|
||||
}
|
||||
}
|
||||
|
||||
// The bash tool reports failures by appending a status line to the result
|
||||
// text rather than exposing a numeric exit code, so recover it from there.
|
||||
function exitCodeFromResult(result: unknown, isError: boolean): number {
|
||||
if (!isError) return 0;
|
||||
|
||||
const content = (result as { content?: unknown } | undefined)?.content;
|
||||
const text = Array.isArray(content)
|
||||
? content
|
||||
.map((part) => {
|
||||
const t = (part as { text?: unknown } | undefined)?.text;
|
||||
return typeof t === "string" ? t : "";
|
||||
})
|
||||
.join("\n")
|
||||
: "";
|
||||
|
||||
const exited = text.match(/Command exited with code (\d+)\s*$/);
|
||||
if (exited) return Number(exited[1]);
|
||||
if (/Command aborted\s*$/.test(text)) return 130;
|
||||
if (/Command timed out after \S+ seconds\s*$/.test(text)) return 124;
|
||||
return 1;
|
||||
}
|
||||
|
||||
export default function atuinPiExtension(pi: ExtensionAPI) {
|
||||
// Atuin history IDs for in-flight bash tool calls, keyed by tool call ID.
|
||||
const pending = new Map<string, string>();
|
||||
|
||||
// Observe bash executions through events instead of registering a bash
|
||||
// tool: registering one conflicts with other extensions that provide
|
||||
// their own bash tool (sandboxes, RTK, remote runners), while events
|
||||
// fire no matter which extension's bash tool ends up executing the
|
||||
// command.
|
||||
pi.on("tool_call", async (event, ctx: ExtensionContext) => {
|
||||
if (event.toolName !== "bash") return;
|
||||
|
||||
const command = (event.input as { command?: unknown }).command;
|
||||
if (typeof command !== "string" || command.length === 0) return;
|
||||
|
||||
const historyId = await startHistory(pi, ctx.cwd, command);
|
||||
if (historyId) pending.set(event.toolCallId, historyId);
|
||||
});
|
||||
|
||||
// tool_execution_end also fires when another extension blocks the call,
|
||||
// unlike tool_result, so entries started above are always closed.
|
||||
pi.on("tool_execution_end", async (event, ctx: ExtensionContext) => {
|
||||
const historyId = pending.get(event.toolCallId);
|
||||
if (!historyId) return;
|
||||
pending.delete(event.toolCallId);
|
||||
|
||||
await endHistory(pi, ctx.cwd, historyId, exitCodeFromResult(event.result, event.isError));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user